Bug 1352324 - (Part 3) Update PhoneNumber utility to fulfill Form Autofill. r=MattN
authorLuke Chang <lchang@mozilla.com>
Fri, 26 May 2017 11:46:36 +0800
changeset 369126 e27d693ddcc46ba2440dbb35a96430285870a2be
parent 369125 5ebb632fa7bbc3062db38606c604c86eab0a6ef4
child 369127 27971d8110df15190c751b6583587507bd1bb962
push id32189
push userarchaeopteryx@coole-files.de
push dateMon, 17 Jul 2017 12:05:07 +0000
treeherdermozilla-central@b8783d6fffdb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersMattN
bugs1352324
milestone56.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 1352324 - (Part 3) Update PhoneNumber utility to fulfill Form Autofill. r=MattN - Fixed the path of resources. - Fixed the getter of nationalNumber with national prefix. - Updated to support parsing numbers by countryCode. - Added a getter of countryCode. - Added the licensing header. MozReview-Commit-ID: 6FEIK4KzGVL
browser/extensions/formautofill/jar.mn
browser/extensions/formautofill/phonenumberutils/PhoneNumber.jsm
browser/extensions/formautofill/phonenumberutils/PhoneNumberMetaData.jsm
browser/extensions/formautofill/phonenumberutils/PhoneNumberNormalizer.jsm
--- a/browser/extensions/formautofill/jar.mn
+++ b/browser/extensions/formautofill/jar.mn
@@ -1,15 +1,16 @@
 # 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/.
 
 [features/formautofill@mozilla.org] chrome.jar:
 % resource formautofill %res/
   res/ (*.jsm)
+  res/phonenumberutils/ (phonenumberutils/*.jsm)
 
 % content formautofill %content/
   content/ (content/*)
 
 % skin formautofill classic/1.0 %skin/linux/ os=LikeUnix
 % skin formautofill classic/1.0 %skin/osx/ os=Darwin
 % skin formautofill classic/1.0 %skin/windows/ os=WINNT
 % skin formautofill-shared classic/1.0 %skin/shared/
--- a/browser/extensions/formautofill/phonenumberutils/PhoneNumber.jsm
+++ b/browser/extensions/formautofill/phonenumberutils/PhoneNumber.jsm
@@ -1,25 +1,26 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
+/* This Source Code Form is subject to the terms of the Apache License, Version
+ * 2.0. If a copy of the Apache License was not distributed with this file, You
+ * can obtain one at https://www.apache.org/licenses/LICENSE-2.0 */
 
-// Don't modify this code. Please use:
-// https://github.com/andreasgal/PhoneNumber.js
+// This library came from https://github.com/andreasgal/PhoneNumber.js but will
+// be further maintained by our own in Form Autofill codebase.
 
 "use strict";
 
 this.EXPORTED_SYMBOLS = ["PhoneNumber"];
 
 const Cu = Components.utils;
 
 Cu.import('resource://gre/modules/XPCOMUtils.jsm');
 XPCOMUtils.defineLazyModuleGetter(this, "PHONE_NUMBER_META_DATA",
-                                  "resource://gre/modules/PhoneNumberMetaData.jsm");
+                                  "resource://formautofill/phonenumberutils/PhoneNumberMetaData.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PhoneNumberNormalizer",
-                                  "resource://gre/modules/PhoneNumberNormalizer.jsm");
+                                  "resource://formautofill/phonenumberutils/PhoneNumberNormalizer.jsm");
 this.PhoneNumber = (function (dataBase) {
   // Use strict in our context only - users might not want it
   'use strict';
 
   const MAX_PHONE_NUMBER_LENGTH = 50;
   const NON_ALPHA_CHARS = /[^a-zA-Z]/g;
   const NON_DIALABLE_CHARS = /[^,#+\*\d]/g;
   const NON_DIALABLE_CHARS_ONCE = new RegExp(NON_DIALABLE_CHARS.source);
@@ -194,48 +195,61 @@ this.PhoneNumber = (function (dataBase) 
       return (number == "NA") ? null : number;
     }
     return null;
   }
 
   function NationalNumber(regionMetaData, number) {
     this.region = regionMetaData.region;
     this.regionMetaData = regionMetaData;
-    this.nationalNumber = number;
+    this.number = number;
   }
 
   // NationalNumber represents the result of parsing a phone number. We have
   // three getters on the prototype that format the number in national and
   // international format. Once called, the getters put a direct property
   // onto the object, caching the result.
   NationalNumber.prototype = {
     // +1 949-726-2896
     get internationalFormat() {
-      var value = FormatNumber(this.regionMetaData, this.nationalNumber, true);
+      var value = FormatNumber(this.regionMetaData, this.number, true);
       Object.defineProperty(this, "internationalFormat", { value: value, enumerable: true });
       return value;
     },
     // (949) 726-2896
     get nationalFormat() {
-      var value = FormatNumber(this.regionMetaData, this.nationalNumber, false);
+      var value = FormatNumber(this.regionMetaData, this.number, false);
       Object.defineProperty(this, "nationalFormat", { value: value, enumerable: true });
       return value;
     },
     // +19497262896
     get internationalNumber() {
       var value = this.internationalFormat ? this.internationalFormat.replace(NON_DIALABLE_CHARS, "")
                                            : null;
       Object.defineProperty(this, "internationalNumber", { value: value, enumerable: true });
       return value;
     },
+    // 9497262896
+    get nationalNumber() {
+      var value = this.nationalFormat ? this.nationalFormat.replace(NON_DIALABLE_CHARS, "")
+                                      : null;
+      Object.defineProperty(this, "nationalNumber", { value: value, enumerable: true });
+      return value;
+    },
     // country name 'US'
     get countryName() {
       var value = this.region ? this.region : null;
       Object.defineProperty(this, "countryName", { value: value, enumerable: true });
       return value;
+    },
+    // country code '+1'
+    get countryCode() {
+      var value = this.regionMetaData.countryCode ? "+" + this.regionMetaData.countryCode : null;
+      Object.defineProperty(this, "countryCode", { value: value, enumerable: true });
+      return value;
     }
   };
 
   // Check whether the number is valid for the given region.
   function IsValidNumber(number, md) {
     return md.possiblePattern.test(number);
   }
 
@@ -253,42 +267,73 @@ this.PhoneNumber = (function (dataBase) 
         return cc;
     }
     return null;
   }
 
   // Parse an international number that starts with the country code. Return
   // null if the number is not a valid international number.
   function ParseInternationalNumber(number) {
-    var ret;
-
     // Parse and strip the country code.
     var countryCode = ParseCountryCode(number);
     if (!countryCode)
       return null;
     number = number.substr(countryCode.length);
 
+    return ParseNumberByCountryCode(number, countryCode);
+  }
+
+  function ParseNumberByCountryCode(number, countryCode) {
+    var ret;
+
     // Lookup the meta data for the region (or regions) and if the rest of
     // the number parses for that region, return the parsed number.
     var entry = dataBase[countryCode];
     if (Array.isArray(entry)) {
       for (var n = 0; n < entry.length; ++n) {
         if (typeof entry[n] == "string")
           entry[n] = ParseMetaData(countryCode, entry[n]);
         if (n > 0)
           entry[n].formats = entry[0].formats;
-        ret = ParseNationalNumber(number, entry[n])
+        ret = ParseNationalNumberAndCheckNationalPrefix(number, entry[n]);
         if (ret)
           return ret;
       }
       return null;
     }
     if (typeof entry == "string")
       entry = dataBase[countryCode] = ParseMetaData(countryCode, entry);
-    return ParseNationalNumber(number, entry);
+    return ParseNationalNumberAndCheckNationalPrefix(number, entry);
+  }
+
+  function ParseNationalNumberAndCheckNationalPrefix(number, md) {
+    var ret;
+
+    // This is not an international number. See if its a national one for
+    // the current region. National numbers can start with the national
+    // prefix, or without.
+    if (md.nationalPrefixForParsing) {
+      // Some regions have specific national prefix parse rules. Apply those.
+      var withoutPrefix = number.replace(md.nationalPrefixForParsing,
+                                         md.nationalPrefixTransformRule || '');
+      ret = ParseNationalNumber(withoutPrefix, md)
+      if (ret)
+        return ret;
+    } else {
+      // If there is no specific national prefix rule, just strip off the
+      // national prefix from the beginning of the number (if there is one).
+      var nationalPrefix = md.nationalPrefix;
+      if (nationalPrefix && number.indexOf(nationalPrefix) == 0 &&
+          (ret = ParseNationalNumber(number.substr(nationalPrefix.length), md))) {
+        return ret;
+      }
+    }
+    ret = ParseNationalNumber(number, md)
+    if (ret)
+      return ret;
   }
 
   // Parse a national number for a specific region. Return null if the
   // number is not a valid national number (it might still be a possible
   // number for parts of that region).
   function ParseNationalNumber(number, md) {
     if (!md.possiblePattern.test(number) ||
         !md.nationalPattern.test(number)) {
@@ -310,16 +355,26 @@ this.PhoneNumber = (function (dataBase) 
     // we can't parse international access codes.
     if ((!defaultRegion || defaultRegion === '001') && number[0] !== '+')
       return null;
 
     // Detect and strip leading '+'.
     if (number[0] === '+')
       return ParseInternationalNumber(number.replace(LEADING_PLUS_CHARS_PATTERN, ""));
 
+    // If "defaultRegion" is a country code, use it to parse the number directly.
+    var matches = String(defaultRegion).match(/^\+?(\d+)/);
+    if (matches) {
+      var countryCode = ParseCountryCode(matches[1]);
+      if (!countryCode) {
+        return null;
+      }
+      return ParseNumberByCountryCode(number, countryCode);
+    }
+
     // Lookup the meta data for the given region.
     var md = FindMetaDataForRegion(defaultRegion.toUpperCase());
 
     if (!md) {
       dump("Couldn't find Meta Data for region: " + defaultRegion + "\n");
       return null;
     }
 
@@ -328,36 +383,17 @@ this.PhoneNumber = (function (dataBase) 
     // prefix and flag the number as international.
     if (md.internationalPrefix.test(number)) {
       var possibleNumber = number.replace(md.internationalPrefix, "");
       ret = ParseInternationalNumber(possibleNumber)
       if (ret)
         return ret;
     }
 
-    // This is not an international number. See if its a national one for
-    // the current region. National numbers can start with the national
-    // prefix, or without.
-    if (md.nationalPrefixForParsing) {
-      // Some regions have specific national prefix parse rules. Apply those.
-      var withoutPrefix = number.replace(md.nationalPrefixForParsing,
-                                         md.nationalPrefixTransformRule || '');
-      ret = ParseNationalNumber(withoutPrefix, md)
-      if (ret)
-        return ret;
-    } else {
-      // If there is no specific national prefix rule, just strip off the
-      // national prefix from the beginning of the number (if there is one).
-      var nationalPrefix = md.nationalPrefix;
-      if (nationalPrefix && number.indexOf(nationalPrefix) == 0 &&
-          (ret = ParseNationalNumber(number.substr(nationalPrefix.length), md))) {
-        return ret;
-      }
-    }
-    ret = ParseNationalNumber(number, md)
+    ret = ParseNationalNumberAndCheckNationalPrefix(number, md);
     if (ret)
       return ret;
 
     // Now lets see if maybe its an international number after all, but
     // without '+' or the international prefix.
     ret = ParseInternationalNumber(number)
     if (ret)
       return ret;
--- a/browser/extensions/formautofill/phonenumberutils/PhoneNumberMetaData.jsm
+++ b/browser/extensions/formautofill/phonenumberutils/PhoneNumberMetaData.jsm
@@ -1,8 +1,12 @@
+/* This Source Code Form is subject to the terms of the Apache License, Version
+ * 2.0. If a copy of the Apache License was not distributed with this file, You
+ * can obtain one at https://www.apache.org/licenses/LICENSE-2.0 */
+
 /*
  * This data was generated base on libphonenumber v8.4.1 via the script in
  * https://github.com/andreasgal/PhoneNumber.js
  *
  * The XML format of libphonenumber has changed since v8.4.2 so we can only stay
  * in this version for now.
  */
 
--- a/browser/extensions/formautofill/phonenumberutils/PhoneNumberNormalizer.jsm
+++ b/browser/extensions/formautofill/phonenumberutils/PhoneNumberNormalizer.jsm
@@ -1,13 +1,14 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
+/* This Source Code Form is subject to the terms of the Apache License, Version
+ * 2.0. If a copy of the Apache License was not distributed with this file, You
+ * can obtain one at https://www.apache.org/licenses/LICENSE-2.0 */
 
-// Don't modify this code. Please use:
-// https://github.com/andreasgal/PhoneNumber.js
+// This library came from https://github.com/andreasgal/PhoneNumber.js but will
+// be further maintained by our own in Form Autofill codebase.
 
 "use strict";
 
 this.EXPORTED_SYMBOLS = ["PhoneNumberNormalizer"];
 
 this.PhoneNumberNormalizer = (function() {
   const UNICODE_DIGITS = /[\uFF10-\uFF19\u0660-\u0669\u06F0-\u06F9]/g;
   const VALID_ALPHA_PATTERN = /[a-zA-Z]/g;