Bug 1108183 - Regularize case of language subtags in Accept-Language header. r=mcmanus
authorawake <bugzillafx@gmail.com>
Fri, 05 Dec 2014 19:44:00 +0100
changeset 263557 a653443ce794ffe6f71a814c65160d4784df5747
parent 263556 f30be176edfd2ab07663b0429db260471f84fca2
child 263558 8cd33e72eb47595535b3a747875f81c575baf287
push id4718
push userraliiev@mozilla.com
push dateMon, 11 May 2015 18:39:53 +0000
treeherdermozilla-beta@c20c4ef55f08 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmcmanus
bugs1108183
milestone39.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
Bug 1108183 - Regularize case of language subtags in Accept-Language header. r=mcmanus
netwerk/protocol/http/nsHttpHandler.cpp
netwerk/test/unit/test_header_Accept-Language_case.js
--- a/netwerk/protocol/http/nsHttpHandler.cpp
+++ b/netwerk/protocol/http/nsHttpHandler.cpp
@@ -1557,32 +1557,57 @@ nsHttpHandler::PrefsChanged(nsIPrefBranc
 void
 nsHttpHandler::TimerCallback(nsITimer * aTimer, void * aClosure)
 {
     nsRefPtr<nsHttpHandler> thisObject = static_cast<nsHttpHandler*>(aClosure);
     if (!thisObject->mPipeliningEnabled)
         thisObject->mCapabilities &= ~NS_HTTP_ALLOW_PIPELINING;
 }
 
+/**
+ * Currently, only regularizes the case of subtags.
+ */
 static void
-NormalizeLanguageTag(char *code)
+CanonicalizeLanguageTag(char *languageTag)
 {
-    bool is_region = false;
-    while (*code != '\0')
-    {
-        if (*code == '-') {
-            is_region = true;
+    char *s = languageTag;
+    while (*s != '\0') {
+        *s = nsCRT::ToLower(*s);
+        s++;
+    }
+
+    s = languageTag;
+    bool isFirst = true;
+    bool seenSingleton = false;
+    while (*s != '\0') {
+        char *subTagEnd = strchr(s, '-');
+        if (subTagEnd == nullptr) {
+            subTagEnd = strchr(s, '\0');
+        }
+
+        if (isFirst) {
+            isFirst = false;
+        } else if (seenSingleton) {
+            // Do nothing
         } else {
-            if (is_region) {
-                *code = nsCRT::ToUpper(*code);
-            } else {
-                *code = nsCRT::ToLower(*code);
+            size_t subTagLength = subTagEnd - s;
+            if (subTagLength == 1) {
+                seenSingleton = true;
+            } else if (subTagLength == 2) {
+                *s = nsCRT::ToUpper(*s);
+                *(s + 1) = nsCRT::ToUpper(*(s + 1));
+            } else if (subTagLength == 4) {
+                *s = nsCRT::ToUpper(*s);
             }
         }
-        code++;
+
+        s = subTagEnd;
+        if (*s != '\0') {
+            s++;
+        }
     }
 }
 
 /**
  *  Allocates a C string into that contains a ISO 639 language list
  *  notated with HTTP "q" values for output with a HTTP Accept-Language
  *  header. Previous q values will be stripped because the order of
  *  the langs imply the q value. The q values are calculated by dividing
@@ -1631,17 +1656,17 @@ PrepareAcceptLanguages(const char *i_Acc
     {
         token = net_FindCharNotInSet(token, HTTP_LWS);
         char* trim;
         trim = net_FindCharInSet(token, ";" HTTP_LWS);
         if (trim != (char*)0)  // remove "; q=..." if present
             *trim = '\0';
 
         if (*token != '\0') {
-            NormalizeLanguageTag(token);
+            CanonicalizeLanguageTag(token);
 
             comma = count_n++ != 0 ? "," : ""; // delimiter if not first item
             uint32_t u = QVAL_TO_UINT(q);
 
             // Only display q-value if less than 1.00.
             if (u < 100) {
                 const char *qval_str;
 
--- a/netwerk/test/unit/test_header_Accept-Language_case.js
+++ b/netwerk/test/unit/test_header_Accept-Language_case.js
@@ -3,16 +3,26 @@ Cu.import("resource://gre/modules/Servic
 var testpath = "/bug1054739";
 
 function run_test() {
   let intlPrefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefService).getBranch("intl.");
 
   let oldAcceptLangPref = intlPrefs.getCharPref("accept_languages");
 
   let testData = [
+    ["en",              "en"],
+    ["ast",             "ast"],
+    ["fr-ca",           "fr-CA"],
+    ["zh-yue",          "zh-yue"],
+    ["az-latn",         "az-Latn"],
+    ["sl-nedis",        "sl-nedis"],
+    ["zh-hant-hk",      "zh-Hant-HK"],
+    ["ZH-HANT-HK",      "zh-Hant-HK"],
+    ["en-us-x-priv",    "en-US-x-priv"],
+    ["en-us-x-twain",   "en-US-x-twain"],
     ["de, en-US, en",   "de,en-US;q=0.7,en;q=0.3"],
     ["de,en-us,en",     "de,en-US;q=0.7,en;q=0.3"],
     ["en-US, en",       "en-US,en;q=0.5"],
     ["EN-US;q=0.2, EN", "en-US,en;q=0.5"],
   ];
 
   for (let i = 0; i < testData.length; i++) {
     let acceptLangPref = testData[i][0];