New algorithm for when to display IDNs as Unicode. Bug 722299, r=honza.b
authorSimon Montagu <smontagu@smontagu.org>
Fri, 01 Mar 2013 03:56:44 -0800
changeset 123467 84b9049008e940bcfea9132e82e5866c851edea4
parent 123466 9f2f90b7c78bfa817519c7978e2a5dc4a6df5739
child 123468 d475e6d545209c8cf6c8e9e43a7dbaad769f6ab9
push id24382
push userryanvm@gmail.com
push dateFri, 01 Mar 2013 23:43:19 +0000
treeherdermozilla-central@3362afba690e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewershonza
bugs722299
milestone22.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
New algorithm for when to display IDNs as Unicode. Bug 722299, r=honza.b
modules/libpref/src/init/all.js
netwerk/dns/nsIDNService.cpp
netwerk/dns/nsIDNService.h
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -1047,20 +1047,28 @@ pref("network.enableIDN", true);
 
 // This preference, if true, causes all UTF-8 domain names to be normalized to
 // punycode.  The intention is to allow UTF-8 domain names as input, but never
 // generate them from punycode.
 pref("network.IDN_show_punycode", false);
 
 // TLDs with "network.IDN.whitelist.tld" explicitly set to true are treated as 
 // IDN-safe. Otherwise, they're treated as unsafe and punycode will be used
-// for displaying them in the UI (e.g. URL bar). Note that these preferences
-// are referred to ONLY when "network.IDN_show_punycode" is false. In other
-// words, all IDNs will be shown in punycode if "network.IDN_show_punycode"
-// is true.
+// for displaying them in the UI (e.g. URL bar), unless they conform to one of 
+// the profiles specified in
+// http://www.unicode.org/reports/tr36/proposed.html#Security_Levels_and_Alerts
+// If "network.IDN.restriction_profile" is "high", the Highly Restrictive
+// profile is used.
+// If "network.IDN.restriction_profile" is "moderate", the Moderately
+// Restrictive profile is used.
+// In all other cases, the ASCII-Only profile is used.
+// Note that these preferences are referred to ONLY when
+// "network.IDN_show_punycode" is false. In other words, all IDNs will be shown
+// in punycode if "network.IDN_show_punycode" is true.
+pref("network.IDN.restriction_profile", "moderate");
 
 // ccTLDs
 pref("network.IDN.whitelist.ac", true);
 pref("network.IDN.whitelist.ar", true);
 pref("network.IDN.whitelist.at", true);
 pref("network.IDN.whitelist.br", true);
 pref("network.IDN.whitelist.ca", true);
 pref("network.IDN.whitelist.ch", true);
--- a/netwerk/dns/nsIDNService.cpp
+++ b/netwerk/dns/nsIDNService.cpp
@@ -2,35 +2,41 @@
 /* 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 "nsIDNService.h"
 #include "nsReadableUtils.h"
 #include "nsCRT.h"
 #include "nsUnicharUtils.h"
+#include "nsUnicodeProperties.h"
+#include "nsUnicodeScriptCodes.h"
+#include "harfbuzz/hb.h"
 #include "nsIServiceManager.h"
 #include "nsIPrefService.h"
 #include "nsIPrefBranch.h"
 #include "nsIObserverService.h"
 #include "nsISupportsPrimitives.h"
 #include "punycode.h"
 
 
+using namespace mozilla::unicode;
+
 //-----------------------------------------------------------------------------
 // RFC 1034 - 3.1. Name space specifications and terminology
 static const uint32_t kMaxDNSNodeLen = 63;
 
 //-----------------------------------------------------------------------------
 
 #define NS_NET_PREF_IDNTESTBED      "network.IDN_testbed"
 #define NS_NET_PREF_IDNPREFIX       "network.IDN_prefix"
 #define NS_NET_PREF_IDNBLACKLIST    "network.IDN.blacklist_chars"
 #define NS_NET_PREF_SHOWPUNYCODE    "network.IDN_show_punycode"
 #define NS_NET_PREF_IDNWHITELIST    "network.IDN.whitelist."
+#define NS_NET_PREF_IDNRESTRICTION  "network.IDN.restriction_profile"
 
 inline bool isOnlySafeChars(const nsAFlatString& in,
                               const nsAFlatString& blacklist)
 {
   return (blacklist.IsEmpty() ||
           in.FindCharInSet(blacklist) == kNotFound);
 }
 
@@ -51,16 +57,17 @@ nsresult nsIDNService::Init()
     prefs->GetBranch(NS_NET_PREF_IDNWHITELIST, getter_AddRefs(mIDNWhitelistPrefBranch));
 
   nsCOMPtr<nsIPrefBranch> prefInternal(do_QueryInterface(prefs));
   if (prefInternal) {
     prefInternal->AddObserver(NS_NET_PREF_IDNTESTBED, this, true); 
     prefInternal->AddObserver(NS_NET_PREF_IDNPREFIX, this, true); 
     prefInternal->AddObserver(NS_NET_PREF_IDNBLACKLIST, this, true);
     prefInternal->AddObserver(NS_NET_PREF_SHOWPUNYCODE, this, true);
+    prefInternal->AddObserver(NS_NET_PREF_IDNRESTRICTION, this, true);
     prefsChanged(prefInternal, nullptr);
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP nsIDNService::Observe(nsISupports *aSubject,
                                     const char *aTopic,
@@ -97,16 +104,30 @@ void nsIDNService::prefsChanged(nsIPrefB
     else
       mIDNBlacklist.Truncate();
   }
   if (!pref || NS_LITERAL_STRING(NS_NET_PREF_SHOWPUNYCODE).Equals(pref)) {
     bool val;
     if (NS_SUCCEEDED(prefBranch->GetBoolPref(NS_NET_PREF_SHOWPUNYCODE, &val)))
       mShowPunycode = val;
   }
+  if (!pref || NS_LITERAL_STRING(NS_NET_PREF_IDNRESTRICTION).Equals(pref)) {
+    nsXPIDLCString profile;
+    if (NS_FAILED(prefBranch->GetCharPref(NS_NET_PREF_IDNRESTRICTION,
+                                          getter_Copies(profile)))) {
+      profile.Truncate();
+    }
+    if (profile.Equals(NS_LITERAL_CSTRING("moderate"))) {
+      mRestrictionProfile = eModeratelyRestrictiveProfile;
+    } else if (profile.Equals(NS_LITERAL_CSTRING("high"))) {
+      mRestrictionProfile = eHighlyRestrictiveProfile;
+    } else {
+      mRestrictionProfile = eASCIIOnlyProfile;
+    }
+  }
 }
 
 nsIDNService::nsIDNService()
 {
   // initialize to the official prefix (RFC 3490 "5. ACE prefix")
   const char kIDNSPrefix[] = "xn--";
   strcpy(mACEPrefix, kIDNSPrefix);
 
@@ -122,20 +143,25 @@ nsIDNService::nsIDNService()
 nsIDNService::~nsIDNService()
 {
   idn_nameprep_destroy(mNamePrepHandle);
 }
 
 /* ACString ConvertUTF8toACE (in AUTF8String input); */
 NS_IMETHODIMP nsIDNService::ConvertUTF8toACE(const nsACString & input, nsACString & ace)
 {
-  return UTF8toACE(input, ace, true);
+  return UTF8toACE(input, ace, true, true);
 }
 
-nsresult nsIDNService::UTF8toACE(const nsACString & input, nsACString & ace, bool allowUnassigned)
+nsresult nsIDNService::SelectiveUTF8toACE(const nsACString& input, nsACString& ace)
+{
+  return UTF8toACE(input, ace, true, false);
+}
+
+nsresult nsIDNService::UTF8toACE(const nsACString & input, nsACString & ace, bool allowUnassigned, bool convertAllLabels)
 {
   nsresult rv;
   NS_ConvertUTF8toUTF16 ustr(input);
 
   // map ideographic period to ASCII period etc.
   normalizeFullStops(ustr);
 
 
@@ -149,87 +175,87 @@ nsresult nsIDNService::UTF8toACE(const n
   ustr.EndReading(end); 
   ace.Truncate();
 
   // encode nodes if non ASCII
   while (start != end) {
     len++;
     if (*start++ == (PRUnichar)'.') {
       rv = stringPrepAndACE(Substring(ustr, offset, len - 1), encodedBuf,
-                            allowUnassigned);
+                            allowUnassigned, convertAllLabels);
       NS_ENSURE_SUCCESS(rv, rv);
 
       ace.Append(encodedBuf);
       ace.Append('.');
       offset += len;
       len = 0;
     }
   }
 
   // add extra node for multilingual test bed
   if (mMultilingualTestBed)
     ace.AppendLiteral("mltbd.");
   // encode the last node if non ASCII
   if (len) {
     rv = stringPrepAndACE(Substring(ustr, offset, len), encodedBuf,
-                          allowUnassigned);
+                          allowUnassigned, convertAllLabels);
     NS_ENSURE_SUCCESS(rv, rv);
 
     ace.Append(encodedBuf);
   }
 
   return NS_OK;
 }
 
 /* AUTF8String convertACEtoUTF8(in ACString input); */
 NS_IMETHODIMP nsIDNService::ConvertACEtoUTF8(const nsACString & input, nsACString & _retval)
 {
-  return ACEtoUTF8(input, _retval, true);
+  return ACEtoUTF8(input, _retval, true, true);
+}
+
+nsresult nsIDNService::SelectiveACEtoUTF8(const nsACString& input, nsACString& _retval)
+{
+  return ACEtoUTF8(input, _retval, false, false);
 }
 
 nsresult nsIDNService::ACEtoUTF8(const nsACString & input, nsACString & _retval,
-                                 bool allowUnassigned)
+                                 bool allowUnassigned, bool convertAllLabels)
 {
   // RFC 3490 - 4.2 ToUnicode
   // ToUnicode never fails.  If any step fails, then the original input
   // sequence is returned immediately in that step.
 
-  if (!IsASCII(input)) {
-    _retval.Assign(input);
-    return NS_OK;
-  }
-  
   uint32_t len = 0, offset = 0;
   nsAutoCString decodedBuf;
 
   nsACString::const_iterator start, end;
   input.BeginReading(start); 
   input.EndReading(end); 
   _retval.Truncate();
 
   // loop and decode nodes
   while (start != end) {
     len++;
     if (*start++ == '.') {
       if (NS_FAILED(decodeACE(Substring(input, offset, len - 1), decodedBuf,
-                              allowUnassigned))) {
+                              allowUnassigned, convertAllLabels))) {
         _retval.Assign(input);
         return NS_OK;
       }
 
       _retval.Append(decodedBuf);
       _retval.Append('.');
       offset += len;
       len = 0;
     }
   }
   // decode the last node
   if (len) {
     if (NS_FAILED(decodeACE(Substring(input, offset, len), decodedBuf,
-                            allowUnassigned)))
+                            allowUnassigned, convertAllLabels)))
       _retval.Assign(input);
     else
       _retval.Append(decodedBuf);
   }
 
   return NS_OK;
 }
 
@@ -296,53 +322,75 @@ NS_IMETHODIMP nsIDNService::Normalize(co
   return NS_OK;
 }
 
 NS_IMETHODIMP nsIDNService::ConvertToDisplayIDN(const nsACString & input, bool * _isASCII, nsACString & _retval)
 {
   // If host is ACE, then convert to UTF-8 if the host is in the IDN whitelist.
   // Else, if host is already UTF-8, then make sure it is normalized per IDN.
 
-  nsresult rv;
+  nsresult rv = NS_OK;
+
+  // Even if the hostname is not ASCII, individual labels may still be ACE, so
+  // test IsACE before testing IsASCII
+  bool isACE;
+  IsACE(input, &isACE);
 
   if (IsASCII(input)) {
     // first, canonicalize the host to lowercase, for whitelist lookup
     _retval = input;
     ToLowerCase(_retval);
 
-    bool isACE;
-    IsACE(_retval, &isACE);
-
-    if (isACE && !mShowPunycode && isInWhitelist(_retval)) {
+    if (isACE && !mShowPunycode) {
       // ACEtoUTF8() can't fail, but might return the original ACE string
       nsAutoCString temp(_retval);
-      ACEtoUTF8(temp, _retval, false);
+      if (isInWhitelist(temp)) {
+        // If the domain is in the whitelist, return the host in UTF-8
+        ACEtoUTF8(temp, _retval, false, true);
+      } else {
+        // Otherwise convert from ACE to UTF8 only those labels which are
+        // considered safe for display
+        SelectiveACEtoUTF8(temp, _retval);
+      }
       *_isASCII = IsASCII(_retval);
     } else {
       *_isASCII = true;
     }
   } else {
     // We have to normalize the hostname before testing against the domain
     // whitelist (see bug 315411), and to ensure the entire string gets
     // normalized.
-    rv = Normalize(input, _retval);
+    //
+    // Normalization and the tests for safe display below, assume that the
+    // input is Unicode, so first convert any ACE labels to UTF8
+    if (isACE) {
+      nsAutoCString temp;
+      ACEtoUTF8(input, temp, false, true);
+      rv = Normalize(temp, _retval);
+    } else {
+      rv = Normalize(input, _retval);
+    }
     if (NS_FAILED(rv)) return rv;
 
     if (mShowPunycode && NS_SUCCEEDED(ConvertUTF8toACE(_retval, _retval))) {
       *_isASCII = true;
       return NS_OK;
     }
 
     // normalization could result in an ASCII-only hostname. alternatively, if
     // the host is converted to ACE by the normalizer, then the host may contain
     // unsafe characters, so leave it ACE encoded. see bug 283016, bug 301694, and bug 309311.
     *_isASCII = IsASCII(_retval);
     if (!*_isASCII && !isInWhitelist(_retval)) {
-      *_isASCII = true;
-      return ConvertUTF8toACE(_retval, _retval);
+      // SelectiveUTF8toACE may return a domain name where some labels are in UTF-8
+      // and some are in ACE, depending on whether they are considered safe for
+      // display
+      rv = SelectiveUTF8toACE(_retval, _retval);
+      *_isASCII = IsASCII(_retval);
+      return rv;
     }
   }
 
   return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 
@@ -537,29 +585,32 @@ nsresult nsIDNService::encodeToACE(const
   if (!strcmp("bq--", mACEPrefix))
     return encodeToRACE(mACEPrefix, in, out);
   
   // use punycoce
   return punycode(mACEPrefix, in, out);
 }
 
 nsresult nsIDNService::stringPrepAndACE(const nsAString& in, nsACString& out,
-                                        bool allowUnassigned)
+                                        bool allowUnassigned,
+                                        bool convertAllLabels)
 {
   nsresult rv = NS_OK;
 
   out.Truncate();
 
   if (in.Length() > kMaxDNSNodeLen) {
     NS_WARNING("IDN node too large");
     return NS_ERROR_FAILURE;
   }
 
   if (IsASCII(in))
     LossyCopyUTF16toASCII(in, out);
+  else if (!convertAllLabels && isLabelSafe(in))
+    CopyUTF16toUTF8(in, out);
   else {
     nsAutoString strPrep;
     rv = stringPrep(in, strPrep, allowUnassigned);
     if (NS_SUCCEEDED(rv)) {
       if (IsASCII(strPrep))
         LossyCopyUTF16toASCII(strPrep, out);
       else
         rv = encodeToACE(strPrep, out);
@@ -598,17 +649,17 @@ void nsIDNService::normalizeFullStops(ns
         break;
     }
     start++;
     index++;
   }
 }
 
 nsresult nsIDNService::decodeACE(const nsACString& in, nsACString& out,
-                                 bool allowUnassigned)
+                                 bool allowUnassigned, bool convertAllLabels)
 {
   bool isAce;
   IsACE(in, &isAce);
   if (!isAce) {
     out.Assign(in);
     return NS_OK;
   }
 
@@ -628,38 +679,42 @@ nsresult nsIDNService::decodeACE(const n
     return NS_ERROR_FAILURE;
   }
 
   // UCS4 -> UTF8
   output[output_length] = 0;
   nsAutoString utf16;
   ucs4toUtf16(output, utf16);
   delete [] output;
+  if (!convertAllLabels && !isLabelSafe(utf16)) {
+    out.Assign(in);
+    return NS_OK;
+  }
   if (!isOnlySafeChars(utf16, mIDNBlacklist))
     return NS_ERROR_FAILURE;
   CopyUTF16toUTF8(utf16, out);
 
   // Validation: encode back to ACE and compare the strings
   nsAutoCString ace;
-  nsresult rv = UTF8toACE(out, ace, allowUnassigned);
+  nsresult rv = UTF8toACE(out, ace, allowUnassigned, true);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (!ace.Equals(in, nsCaseInsensitiveCStringComparator()))
     return NS_ERROR_FAILURE;
 
   return NS_OK;
 }
 
 bool nsIDNService::isInWhitelist(const nsACString &host)
 {
   if (mIDNWhitelistPrefBranch) {
     nsAutoCString tld(host);
     // make sure the host is ACE for lookup and check that there are no
     // unassigned codepoints
-    if (!IsASCII(tld) && NS_FAILED(UTF8toACE(tld, tld, false))) {
+    if (!IsASCII(tld) && NS_FAILED(UTF8toACE(tld, tld, false, true))) {
       return false;
     }
 
     // truncate trailing dots first
     tld.Trim(".");
     int32_t pos = tld.RFind(".");
     if (pos == kNotFound)
       return false;
@@ -669,8 +724,178 @@ bool nsIDNService::isInWhitelist(const n
     bool safe;
     if (NS_SUCCEEDED(mIDNWhitelistPrefBranch->GetBoolPref(tld.get(), &safe)))
       return safe;
   }
 
   return false;
 }
 
+bool nsIDNService::isLabelSafe(const nsAString &label)
+{
+  // We should never get here if the label is ASCII
+  NS_ASSERTION(!IsASCII(label), "ASCII label in IDN checking");
+  if (mRestrictionProfile == eASCIIOnlyProfile) {
+    return false;
+  }
+
+  nsAString::const_iterator current, end;
+  label.BeginReading(current);
+  label.EndReading(end);
+
+  int32_t lastScript = MOZ_SCRIPT_INVALID;
+  uint32_t previousChar = 0;
+  uint32_t savedNumberingSystem = 0;
+  HanVariantType savedHanVariant = HVT_NotHan;
+
+  int32_t savedScript = -1;
+
+  while (current != end) {
+    uint32_t ch = *current++;
+
+    if (NS_IS_HIGH_SURROGATE(ch) && current != end &&
+        NS_IS_LOW_SURROGATE(*current)) {
+      ch = SURROGATE_TO_UCS4(ch, *current++);
+    }
+
+    // Check for restricted characters; aspirational scripts are permitted
+    XidmodType xm = GetIdentifierModification(ch);
+    int32_t script = GetScriptCode(ch);
+    if (xm > XIDMOD_RECOMMENDED &&
+        !(xm == XIDMOD_LIMITED_USE &&
+          (script == MOZ_SCRIPT_CANADIAN_ABORIGINAL ||
+           script == MOZ_SCRIPT_MIAO ||
+           script == MOZ_SCRIPT_MONGOLIAN ||
+           script == MOZ_SCRIPT_TIFINAGH ||
+           script == MOZ_SCRIPT_YI))) {
+      return false;
+    }
+
+    // Check for mixed script
+    if (script != MOZ_SCRIPT_COMMON &&
+        script != MOZ_SCRIPT_INHERITED &&
+        script != lastScript) {
+      if (illegalScriptCombo(script, savedScript)) {
+        return false;
+      }
+      lastScript = script;
+    }
+
+    // Check for mixed numbering systems
+    if (GetGeneralCategory(ch) ==
+        HB_UNICODE_GENERAL_CATEGORY_DECIMAL_NUMBER) {
+      uint32_t zeroCharacter = ch - GetNumericValue(ch);
+      if (savedNumberingSystem == 0) {
+        // If we encounter a decimal number, save the zero character from that
+        // numbering system.
+        savedNumberingSystem = zeroCharacter;
+      } else if (zeroCharacter != savedNumberingSystem) {
+        return false;
+      }
+    }
+
+    // Check for consecutive non-spacing marks
+    if (previousChar != 0 &&
+        previousChar == ch &&
+        GetGeneralCategory(ch) == HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK) {
+      return false;
+    }
+
+    // Check for both simplified-only and traditional-only Chinese characters
+    HanVariantType hanVariant = GetHanVariant(ch);
+    if (hanVariant == HVT_SimplifiedOnly || hanVariant == HVT_TraditionalOnly) {
+      if (savedHanVariant == HVT_NotHan) {
+        savedHanVariant = hanVariant;
+      } else if (hanVariant != savedHanVariant)  {
+        return false;
+      }
+    }
+
+    previousChar = ch;
+  }
+  return true;
+}
+
+// Scripts that we care about in illegalScriptCombo
+static const int32_t scriptTable[] = {
+  MOZ_SCRIPT_BOPOMOFO, MOZ_SCRIPT_CYRILLIC, MOZ_SCRIPT_GREEK,
+  MOZ_SCRIPT_HANGUL,   MOZ_SCRIPT_HAN,      MOZ_SCRIPT_HIRAGANA,
+  MOZ_SCRIPT_KATAKANA, MOZ_SCRIPT_LATIN };
+
+#define BOPO 0
+#define CYRL 1
+#define GREK 2
+#define HANG 3
+#define HANI 4
+#define HIRA 5
+#define KATA 6
+#define LATN 7
+#define OTHR 8
+#define JPAN 9    // Latin + Han + Hiragana + Katakana
+#define CHNA 10   // Latin + Han + Bopomofo
+#define KORE 11   // Latin + Han + Hangul
+#define HNLT 12   // Latin + Han (could be any of the above combinations)
+#define FAIL 13
+
+static inline int32_t findScriptIndex(int32_t aScript)
+{
+  int32_t tableLength = sizeof(scriptTable) / sizeof(int32_t);
+  for (int32_t index = 0; index < tableLength; ++index) {
+    if (aScript == scriptTable[index]) {
+      return index;
+    }
+  }
+  return OTHR;
+}
+
+static const int32_t scriptComboTable[13][9] = {
+/* thisScript: BOPO  CYRL  GREK  HANG  HANI  HIRA  KATA  LATN  OTHR
+ * savedScript */
+ /* BOPO */  { BOPO, FAIL, FAIL, FAIL, CHNA, FAIL, FAIL, CHNA, FAIL },
+ /* CYRL */  { FAIL, CYRL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL },
+ /* GREK */  { FAIL, FAIL, GREK, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL },
+ /* HANG */  { FAIL, FAIL, FAIL, HANG, KORE, FAIL, FAIL, KORE, FAIL },
+ /* HANI */  { CHNA, FAIL, FAIL, KORE, HANI, JPAN, JPAN, HNLT, FAIL },
+ /* HIRA */  { FAIL, FAIL, FAIL, FAIL, JPAN, HIRA, JPAN, JPAN, FAIL },
+ /* KATA */  { FAIL, FAIL, FAIL, FAIL, JPAN, JPAN, KATA, JPAN, FAIL },
+ /* LATN */  { CHNA, FAIL, FAIL, KORE, HNLT, JPAN, JPAN, LATN, OTHR },
+ /* OTHR */  { FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, FAIL, OTHR, FAIL },
+ /* JPAN */  { FAIL, FAIL, FAIL, FAIL, JPAN, JPAN, JPAN, JPAN, FAIL },
+ /* CHNA */  { CHNA, FAIL, FAIL, FAIL, CHNA, FAIL, FAIL, CHNA, FAIL },
+ /* KORE */  { FAIL, FAIL, FAIL, KORE, KORE, FAIL, FAIL, KORE, FAIL },
+ /* HNLT */  { CHNA, FAIL, FAIL, KORE, HNLT, JPAN, JPAN, HNLT, FAIL }
+};
+
+bool nsIDNService::illegalScriptCombo(int32_t script, int32_t& savedScript)
+{
+  if (savedScript == -1) {
+    savedScript = findScriptIndex(script);
+    return false;
+  }
+
+  savedScript = scriptComboTable[savedScript] [findScriptIndex(script)];
+  /*
+   * Special case combinations that depend on which profile is in use
+   * In the Highly Restrictive profile Latin is not allowed with any
+   *  other script
+   *
+   * In the Moderately Restrictive profile Latin mixed with any other
+   *  single script is allowed.
+   */
+  return ((savedScript == OTHR &&
+           mRestrictionProfile == eHighlyRestrictiveProfile) ||
+          savedScript == FAIL);
+}
+
+#undef BOPO
+#undef CYRL
+#undef GREK
+#undef HANG
+#undef HANI
+#undef HIRA
+#undef KATA
+#undef LATN
+#undef OTHR
+#undef JPAN
+#undef CHNA
+#undef KORE
+#undef HNLT
+#undef FAIL
--- a/netwerk/dns/nsIDNService.h
+++ b/netwerk/dns/nsIDNService.h
@@ -34,31 +34,41 @@ public:
   nsIDNService();
   virtual ~nsIDNService();
 
   nsresult Init();
 
 private:
   void normalizeFullStops(nsAString& s);
   nsresult stringPrepAndACE(const nsAString& in, nsACString& out,
-                            bool allowUnassigned);
+                            bool allowUnassigned, bool convertAllLabels);
   nsresult encodeToACE(const nsAString& in, nsACString& out);
   nsresult stringPrep(const nsAString& in, nsAString& out,
                       bool allowUnassigned);
   nsresult decodeACE(const nsACString& in, nsACString& out,
-                     bool allowUnassigned);
-  nsresult UTF8toACE(const nsACString& in, nsACString& out,
-                     bool allowUnassigned);
-  nsresult ACEtoUTF8(const nsACString& in, nsACString& out,
-                     bool allowUnassigned);
+                     bool allowUnassigned, bool convertAllLabels);
+  nsresult SelectiveUTF8toACE(const nsACString& input, nsACString& ace);
+  nsresult SelectiveACEtoUTF8(const nsACString& input, nsACString& _retval);
+  nsresult UTF8toACE(const nsACString& input, nsACString& ace,
+                     bool allowUnassigned, bool convertAllLabels);
+  nsresult ACEtoUTF8(const nsACString& input, nsACString& _retval,
+                     bool allowUnassigned, bool convertAllLabels);
   bool isInWhitelist(const nsACString &host);
   void prefsChanged(nsIPrefBranch *prefBranch, const PRUnichar *pref);
+  bool isLabelSafe(const nsAString &label);
+  bool illegalScriptCombo(int32_t script, int32_t& savedScript);
 
   bool mMultilingualTestBed;  // if true generates extra node for multilingual testbed 
   idn_nameprep_t mNamePrepHandle;
   nsCOMPtr<nsIUnicodeNormalizer> mNormalizer;
   char mACEPrefix[kACEPrefixLen+1];
   nsXPIDLString mIDNBlacklist;
   bool mShowPunycode;
+  enum restrictionProfile {
+    eASCIIOnlyProfile,
+    eHighlyRestrictiveProfile,
+    eModeratelyRestrictiveProfile
+  };
+  restrictionProfile mRestrictionProfile;
   nsCOMPtr<nsIPrefBranch> mIDNWhitelistPrefBranch;
 };
 
 #endif  // nsIDNService_h__