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 idunknown
push userunknown
push dateunknown
reviewershonza.b
bugs722299
milestone22.0a1
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__