Bug 1352324 - (Part 3) Update PhoneNumber utility to fulfill Form Autofill. r=MattN draft
authorLuke Chang <lchang@mozilla.com>
Fri, 26 May 2017 11:46:36 +0800
changeset 609612 50be9a0904429b2e11a107cc967061f0108d5107
parent 609611 cc38f56de6b777b7d1825c0eb522769b60dc19b4
child 609613 b6681b99ad2adbcb0e0eacb69c36233945acf2f9
child 609616 d642a87ac21cc063ce7f609159300f8ec9f3c2c2
push id68601
push userbmo:lchang@mozilla.com
push dateMon, 17 Jul 2017 03:22:47 +0000
reviewersMattN
bugs1352324
milestone56.0a1
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;