Bug 883923 - Provide a Fuzzy Matcher API for phone numbers. r=fabrice

This commit is contained in:
Michael Henretty 2013-07-31 21:04:59 -07:00
parent 532b81527e
commit 2258001442
19 changed files with 471 additions and 43 deletions

View File

@ -348,6 +348,8 @@
@BINPATH@/components/BrowserElementParent.js
@BINPATH@/components/ContactManager.js
@BINPATH@/components/ContactManager.manifest
@BINPATH@/components/PhoneNumberService.js
@BINPATH@/components/PhoneNumberService.manifest
@BINPATH@/components/PermissionSettings.js
@BINPATH@/components/PermissionSettings.manifest
@BINPATH@/components/PermissionPromptService.js

View File

@ -520,6 +520,8 @@
@BINPATH@/components/PermissionSettings.manifest
@BINPATH@/components/ContactManager.js
@BINPATH@/components/ContactManager.manifest
@BINPATH@/components/PhoneNumberService.js
@BINPATH@/components/PhoneNumberService.manifest
@BINPATH@/components/AlarmsManager.js
@BINPATH@/components/AlarmsManager.manifest
@BINPATH@/components/Push.js

View File

@ -145,6 +145,11 @@ this.PermissionsTable = { geolocation: {
privileged: DENY_ACTION,
certified: ALLOW_ACTION
},
phonenumberservice: {
app: DENY_ACTION,
privileged: DENY_ACTION,
certified: ALLOW_ACTION
},
fmradio: {
app: DENY_ACTION,
privileged: ALLOW_ACTION,

View File

@ -1602,6 +1602,14 @@ Navigator::HasPowerSupport(JSContext* /* unused */, JSObject* aGlobal)
return win && PowerManager::CheckPermission(win);
}
/* static */
bool
Navigator::HasPhoneNumberSupport(JSContext* /* unused */, JSObject* aGlobal)
{
nsCOMPtr<nsPIDOMWindow> win = GetWindowFromGlobal(aGlobal);
return CheckPermission(win, "phonenumberservice");
}
/* static */
bool
Navigator::HasIdleSupport(JSContext* /* unused */, JSObject* aGlobal)

View File

@ -256,6 +256,7 @@ public:
// WebIDL helper methods
static bool HasBatterySupport(JSContext* /* unused*/, JSObject* /*unused */);
static bool HasPowerSupport(JSContext* /* unused */, JSObject* aGlobal);
static bool HasPhoneNumberSupport(JSContext* /* unused */, JSObject* aGlobal);
static bool HasIdleSupport(JSContext* /* unused */, JSObject* aGlobal);
static bool HasWakeLockSupport(JSContext* /* unused*/, JSObject* /*unused */);
static bool HasDesktopNotificationSupport(JSContext* /* unused*/,

View File

@ -9,19 +9,18 @@
this.EXPORTED_SYMBOLS = ["PhoneNumber"];
Components.utils.import("resource://gre/modules/PhoneNumberMetaData.jsm");
Components.utils.import("resource://gre/modules/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 UNICODE_DIGITS = /[\uFF10-\uFF19\u0660-\u0669\u06F0-\u06F9]/g;
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);
const BACKSLASH = /\\/g;
const SPLIT_FIRST_GROUP = /^(\d+)(.*)$/;
const VALID_ALPHA_PATTERN = /[a-zA-Z]/g;
const LEADING_PLUS_CHARS_PATTERN = /^[+\uFF0B]+/g;
// Format of the string encoded meta data. If the name contains "^" or "$"
@ -225,40 +224,6 @@ this.PhoneNumber = (function (dataBase) {
}
};
// Map letters to numbers according to the ITU E.161 standard
var E161 = {
'a': 2, 'b': 2, 'c': 2,
'd': 3, 'e': 3, 'f': 3,
'g': 4, 'h': 4, 'i': 4,
'j': 5, 'k': 5, 'l': 5,
'm': 6, 'n': 6, 'o': 6,
'p': 7, 'q': 7, 'r': 7, 's': 7,
't': 8, 'u': 8, 'v': 8,
'w': 9, 'x': 9, 'y': 9, 'z': 9
};
// Normalize a number by converting unicode numbers and symbols to their
// ASCII equivalents and removing all non-dialable characters.
function NormalizeNumber(number, numbersOnly) {
if (typeof number !== 'string') {
return '';
}
number = number.replace(UNICODE_DIGITS,
function (ch) {
return String.fromCharCode(48 + (ch.charCodeAt(0) & 0xf));
});
if (!numbersOnly) {
number = number.replace(VALID_ALPHA_PATTERN,
function (ch) {
return String(E161[ch.toLowerCase()] || 0);
});
}
number = number.replace(LEADING_PLUS_CHARS_PATTERN, "+");
number = number.replace(NON_DIALABLE_CHARS, "");
return number;
}
// Check whether the number is valid for the given region.
function IsValidNumber(number, md) {
return md.possiblePattern.test(number);
@ -329,7 +294,7 @@ this.PhoneNumber = (function (dataBase) {
var ret;
// Remove formating characters and whitespace.
number = NormalizeNumber(number);
number = PhoneNumberNormalizer.Normalize(number);
// If there is no defaultRegion, we can't parse international access codes.
if (!defaultRegion && number[0] !== '+')
@ -404,6 +369,5 @@ this.PhoneNumber = (function (dataBase) {
return {
IsPlain: IsPlainPhoneNumber,
Parse: ParseNumber,
Normalize: NormalizeNumber
};
})(PHONE_NUMBER_META_DATA);

View File

@ -0,0 +1,55 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
// Don't modify this code. Please use:
// https://github.com/andreasgal/PhoneNumber.js
"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;
const LEADING_PLUS_CHARS_PATTERN = /^[+\uFF0B]+/g;
const NON_DIALABLE_CHARS = /[^,#+\*\d]/g;
// Map letters to numbers according to the ITU E.161 standard
var E161 = {
'a': 2, 'b': 2, 'c': 2,
'd': 3, 'e': 3, 'f': 3,
'g': 4, 'h': 4, 'i': 4,
'j': 5, 'k': 5, 'l': 5,
'm': 6, 'n': 6, 'o': 6,
'p': 7, 'q': 7, 'r': 7, 's': 7,
't': 8, 'u': 8, 'v': 8,
'w': 9, 'x': 9, 'y': 9, 'z': 9
};
// Normalize a number by converting unicode numbers and symbols to their
// ASCII equivalents and removing all non-dialable characters.
function NormalizeNumber(number, numbersOnly) {
if (typeof number !== 'string') {
return '';
}
number = number.replace(UNICODE_DIGITS,
function (ch) {
return String.fromCharCode(48 + (ch.charCodeAt(0) & 0xf));
});
if (!numbersOnly) {
number = number.replace(VALID_ALPHA_PATTERN,
function (ch) {
return String(E161[ch.toLowerCase()] || 0);
});
}
number = number.replace(LEADING_PLUS_CHARS_PATTERN, "+");
number = number.replace(NON_DIALABLE_CHARS, "");
return number;
};
return {
Normalize: NormalizeNumber
};
})();

View File

@ -0,0 +1,97 @@
/* 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/. */
"use strict";
const DEBUG = false;
function debug(s) { dump("-*- PhoneNumberService.js: " + s + "\n"); }
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/DOMRequestHelper.jsm");
Cu.import("resource://gre/modules/PhoneNumberUtils.jsm");
Cu.import("resource://gre/modules/PhoneNumberNormalizer.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
"@mozilla.org/childprocessmessagemanager;1",
"nsIMessageSender");
// PhoneNumberService
function PhoneNumberService()
{
if (DEBUG) debug("Constructor");
}
PhoneNumberService.prototype = {
__proto__: DOMRequestIpcHelper.prototype,
receiveMessage: function(aMessage) {
if (DEBUG) debug("receiveMessage: " + aMessage.name);
let msg = aMessage.json;
let req = this.getRequest(msg.requestID);
if (!req) {
return;
}
switch (aMessage.name) {
case "PhoneNumberService:FuzzyMatch:Return:KO":
Services.DOMRequest.fireError(req.request, msg.errorMsg);
break;
case "PhoneNumberService:FuzzyMatch:Return:OK":
Services.DOMRequest.fireSuccess(req.request, msg.result);
break;
default:
if (DEBUG) debug("Wrong message: " + aMessage.name);
}
this.removeRequest(msg.requestID);
},
fuzzyMatch: function(aNumber1, aNumber2) {
if (DEBUG) debug("fuzzyMatch: " + aNumber1 + ", " + aNumber2);
let request = this.createRequest();
if ((aNumber1 && !aNumber2) || (aNumber2 && !aNumber1)) {
// if only one of the numbers is empty/null/undefined and the other
// number is not, we can fire false result in next tick
Services.DOMRequest.fireSuccessAsync(request, false);
} else if ((aNumber1 === aNumber2) ||
(PhoneNumberNormalizer.Normalize(aNumber1) === PhoneNumberNormalizer.Normalize(aNumber2))) {
// if we have a simple match fire successful request in next tick
Services.DOMRequest.fireSuccessAsync(request, true);
} else {
// invoke fuzzy matching in the parent
let options = { number1: aNumber1, number2: aNumber2 };
cpmm.sendAsyncMessage("PhoneNumberService:FuzzyMatch",
{requestID: this.getRequestId({request: request}),
options: options});
}
return request;
},
normalize: function(aNumber) {
if (DEBUG) debug("normalize: " + aNumber);
return PhoneNumberNormalizer.Normalize(aNumber);
},
init: function(aWindow) {
if (DEBUG) debug("init call");
this.initDOMRequestHelper(aWindow, [
"PhoneNumberService:FuzzyMatch:Return:OK",
"PhoneNumberService:FuzzyMatch:Return:KO"
]);
},
classID : Components.ID("{e2768710-eb17-11e2-91e2-0800200c9a66}"),
contractID : "@mozilla.org/phoneNumberService;1",
QueryInterface : XPCOMUtils.generateQI([Ci.nsIDOMGlobalPropertyInitializer]),
}
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([PhoneNumberService]);

View File

@ -0,0 +1,2 @@
component {e2768710-eb17-11e2-91e2-0800200c9a66} PhoneNumberService.js
contract @mozilla.org/phoneNumberService;1 {e2768710-eb17-11e2-91e2-0800200c9a66}

View File

@ -9,10 +9,12 @@ const DEBUG = false;
function debug(s) { if(DEBUG) dump("-*- PhoneNumberutils: " + s + "\n"); }
const Cu = Components.utils;
const Cc = Components.classes;
const Ci = Components.interfaces;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
Cu.import("resource://gre/modules/PhoneNumber.jsm");
Cu.import("resource://gre/modules/PhoneNumberNormalizer.jsm");
Cu.import("resource://gre/modules/mcc_iso3166_table.jsm");
#ifdef MOZ_B2G_RIL
@ -22,6 +24,9 @@ XPCOMUtils.defineLazyServiceGetter(this, "mobileConnection",
#endif
this.PhoneNumberUtils = {
init: function() {
ppmm.addMessageListener(["PhoneNumberService:FuzzyMatch"], this);
},
// 1. See whether we have a network mcc
// 2. If we don't have that, look for the simcard mcc
// 3. If we don't have that or its 0 (not activated), pick up the last used mcc
@ -96,8 +101,62 @@ this.PhoneNumberUtils = {
},
normalize: function Normalize(aNumber, aNumbersOnly) {
var normalized = PhoneNumber.Normalize(aNumber, aNumbersOnly);
let normalized = PhoneNumberNormalizer.Normalize(aNumber, aNumbersOnly);
if (DEBUG) debug("normalize(" + aNumber + "): " + normalized + ", " + aNumbersOnly);
return normalized;
},
fuzzyMatch: function fuzzyMatch(aNumber1, aNumber2) {
let normalized1 = this.normalize(aNumber1);
let normalized2 = this.normalize(aNumber2);
if (DEBUG) debug("Normalized Number1: " + normalized1 + ", Number2: " + normalized2);
if (normalized1 === normalized2) {
return true;
}
let parsed1 = this.parse(aNumber1);
let parsed2 = this.parse(aNumber2);
if (parsed1 && parsed2) {
if (parsed1.internationalNumber === parsed2.internationalNumber
|| parsed1.nationalNumber === parsed2.nationalNumber) {
return true;
}
}
let countryName = this.getCountryName();
let ssPref = "dom.phonenumber.substringmatching." + countryName;
if (Services.prefs.getPrefType(ssPref) == Ci.nsIPrefBranch.PREF_INT) {
let val = Services.prefs.getIntPref(ssPref);
if (normalized1.slice(-val) === normalized2.slice(-val)) {
return true;
}
}
return false;
},
receiveMessage: function(aMessage) {
if (DEBUG) debug("receiveMessage " + aMessage.name);
let mm = aMessage.target;
let msg = aMessage.data;
switch (aMessage.name) {
case "PhoneNumberService:FuzzyMatch":
mm.sendAsyncMessage("PhoneNumberService:FuzzyMatch:Return:OK", {
requestID: msg.requestID,
result: this.fuzzyMatch(msg.options.number1, msg.options.number2)
});
break;
default:
if (DEBUG) debug("WRONG MESSAGE NAME: " + aMessage.name);
}
}
};
let inParent = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime)
.processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
if (inParent) {
Cu.import("resource://gre/modules/PhoneNumber.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
"@mozilla.org/parentprocessmessagemanager;1",
"nsIMessageListenerManager");
PhoneNumberUtils.init();
}

View File

@ -6,13 +6,18 @@
TEST_DIRS += ['tests']
EXTRA_COMPONENTS += [
'PhoneNumberService.js',
'PhoneNumberService.manifest',
]
EXTRA_JS_MODULES += [
'PhoneNumber.jsm',
'PhoneNumberMetaData.jsm',
'PhoneNumberNormalizer.jsm',
'mcc_iso3166_table.jsm',
]
EXTRA_PP_JS_MODULES += [
'PhoneNumberUtils.jsm',
]

View File

@ -11,9 +11,14 @@ relativesrcdir = @relativesrcdir@
include $(DEPTH)/config/autoconf.mk
MOCHITEST_FILES = \
test_phonenumberutils_basics.html \
$(NULL)
MOCHITEST_CHROME_FILES = \
test_phonenumber.xul \
test_phonenumberutils.xul \
test_phonenumberservice.xul \
$(NULL)
include $(topsrcdir)/config/rules.mk

View File

@ -21,6 +21,7 @@
"use strict";
Components.utils.import("resource://gre/modules/PhoneNumber.jsm");
Components.utils.import("resource://gre/modules/PhoneNumberNormalizer.jsm");
function IsPlain(dial, expected) {
var result = PhoneNumber.IsPlain(dial);
@ -32,7 +33,7 @@ function IsPlain(dial, expected) {
}
function Normalize(dial, expected) {
var result = PhoneNumber.Normalize(dial);
var result = PhoneNumberNormalizer.Normalize(dial);
if (result !== expected) {
ok(false, "normalized " + dial + " to " + result + ", expected " + expected + " instead.");
} else {
@ -41,7 +42,7 @@ function Normalize(dial, expected) {
}
function Normalize(dial, expected) {
var result = PhoneNumber.Normalize(dial);
var result = PhoneNumberNormalizer.Normalize(dial);
if (result != expected) {
ok(false, "Normalize error!\n");
print("expected: " + expected);

View File

@ -0,0 +1,158 @@
<?xml version="1.0"?>
<!-- Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ -->
<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
title="Mozilla Bug 781379">
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
<!-- test results are displayed in the html:body -->
<body xmlns="http://www.w3.org/1999/xhtml">
<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=809213"
target="_blank">Mozilla Bug 809213</a>
</body>
<script type="application/javascript;version=1.8">
"use strict";
Components.utils.import("resource://gre/modules/Services.jsm");
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
Services.prefs.setIntPref("dom.phonenumber.substringmatching.BR", 8);
Services.prefs.setCharPref("ril.lastKnownSimMcc", "310");
var pm = SpecialPowers.Cc["@mozilla.org/permissionmanager;1"]
.getService(SpecialPowers.Ci.nsIPermissionManager);
pm.addFromPrincipal(window.document.nodePrincipal, "phonenumberservice",
SpecialPowers.Ci.nsIPermissionManager.ALLOW_ACTION);
function onUnwantedSuccess() {
ok(false, "onUnwantedSuccess: shouldn't get here");
}
function onFailure() {
ok(false, "in on Failure!");
}
var req;
var index = 0;
var mozPhoneNumberService = window.navigator.mozPhoneNumberService;
ok(mozPhoneNumberService, "mozPhoneNumberService exists");
var steps = [
function() {
req = mozPhoneNumberService.fuzzyMatch("123", "123");
req.onsuccess = function(e) {
is(req.result, true, "same number");
next();
};
req.onerror = onFailure;
},
function() {
req = mozPhoneNumberService.fuzzyMatch("abcdef", "222333");
req.onsuccess = function(e) {
is(req.result, true, "normalize first number");
next();
};
req.onerror = onFailure;
},
function() {
req = mozPhoneNumberService.fuzzyMatch("abc333", "222def");
req.onsuccess = function(e) {
is(req.result, true, "normalize first and second number");
next();
};
req.onerror = onFailure;
},
function() {
req = mozPhoneNumberService.fuzzyMatch("1234567", "1234568");
req.onsuccess = function(e) {
is(req.result, false, "different numbers should not match");
next();
};
req.onerror = onFailure;
},
function() {
req = mozPhoneNumberService.fuzzyMatch("1234567", "123456");
req.onsuccess = function(e) {
is(req.result, false, "different length numbers should not match");
next();
};
req.onerror = onFailure;
},
function() {
req = mozPhoneNumberService.fuzzyMatch("1234567", "123456---");
req.onsuccess = function(e) {
is(req.result, false, "invalid number should not match valid number");
next();
};
req.onerror = onFailure;
},
function() {
req = mozPhoneNumberService.fuzzyMatch("111", undefined);
req.onsuccess = function(e) {
is(req.result, false, "missing second argument should not match");
next();
};
req.onerror = onFailure;
},
function() {
req = mozPhoneNumberService.fuzzyMatch(undefined, "111");
req.onsuccess = function(e) {
is(req.result, false, "missing first argument should not match");
next();
};
req.onerror = onFailure;
},
function() {
req = mozPhoneNumberService.fuzzyMatch(null, "");
req.onsuccess = function(e) {
is(req.result, true, "missing first argument should fuzzy match empty string");
next();
};
req.onerror = onFailure;
},
function() {
req = mozPhoneNumberService.fuzzyMatch("+552155555555", "2155555555");
req.onsuccess = function(e) {
is(req.result, true, "test internationalization of number");
next();
};
req.onerror = onFailure;
},
function() {
req = mozPhoneNumberService.fuzzyMatch("aaa123456789", "zzzzz123456789");
req.onsuccess = function(e) {
is(req.result, true, "substring matching should be in effect");
next();
};
req.onerror = onFailure;
},
function () {
ok(true, "all done!\n");
SimpleTest.finish();
}
];
function next() {
ok(true, "Begin!");
if (index >= steps.length) {
ok(false, "Shouldn't get here!");
return;
}
try {
var i = index++;
steps[i]();
} catch(ex) {
ok(false, "Caught exception", ex);
}
}
SimpleTest.waitForExplicitFinish();
addLoadEvent(next);
</script>
</window>

View File

@ -20,8 +20,12 @@
"use strict";
Components.utils.import("resource://gre/modules/Services.jsm");
Components.utils.import("resource://gre/modules/PhoneNumberUtils.jsm");
Services.prefs.setIntPref("dom.phonenumber.substringmatching.BR", 8);
Services.prefs.setCharPref("ril.lastKnownSimMcc", "310");
function CantParseWithMcc(dial, mcc) {
var result = PhoneNumberUtils.parseWithMCC(dial, mcc);
if (result) {
@ -43,6 +47,11 @@ function ParseWithMcc(dial, mcc) {
}
}
function FuzzyMatch(number1, number2, expect) {
var result = PhoneNumberUtils.fuzzyMatch(number1, number2);
is(result, expect, "FuzzyMatch OK!");
}
// Unknown mcc
CantParseWithMcc("1234", 123);
ParseWithMcc("4165555555", 302);
@ -50,5 +59,10 @@ ParseWithMcc("4165555555", 302);
is(PhoneNumberUtils.normalize("123abc", true), "123", "NumbersOnly");
is(PhoneNumberUtils.normalize("123abc", false), "123222", "NumbersOnly");
FuzzyMatch("123abc", "123222", true);
FuzzyMatch("123456789", "123456789", true);
FuzzyMatch("111", null, false);
FuzzyMatch("+552155555555", "2155555555", true);
FuzzyMatch("aaa123456789", "zzzzz123456789", true);
</script>
</window>

View File

@ -0,0 +1,32 @@
<!DOCTYPE html>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=674720
-->
<head>
<title>Test for Bug 883923 PhoneNumberUtils FuzzyMatch</title>
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=883923">Mozilla Bug 883923</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script class="testbody" type="text/javascript">
"use strict";
var mozPhoneNumberService = window.navigator.mozPhoneNumberService;
if (mozPhoneNumberService) {
is(mozPhoneNumberService.fuzzyMatch, undefined, "Fuzzy Match should not be accesible");
is(mozPhoneNumberService.normalize, undefined, "Normalize should not be accessible");
}
</script>
</pre>
</body>
</html>

View File

@ -0,0 +1,15 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/.
*/
[JSImplementation="@mozilla.org/phoneNumberService;1", NavigatorProperty="mozPhoneNumberService"]
interface PhoneNumberService {
[Func="Navigator::HasPhoneNumberSupport"]
DOMRequest fuzzyMatch([TreatNullAs=EmptyString, TreatUndefinedAs=EmptyString] DOMString number1, [TreatNullAs=EmptyString, TreatUndefinedAs=EmptyString] DOMString number2);
[Func="Navigator::HasPhoneNumberSupport"]
DOMString normalize(DOMString number);
};

View File

@ -224,6 +224,7 @@ webidl_files = \
PerformanceNavigation.webidl \
PerformanceTiming.webidl \
PeriodicWave.webidl \
PhoneNumberService.webidl \
Plugin.webidl \
PluginArray.webidl \
Position.webidl \

View File

@ -283,6 +283,8 @@
@BINPATH@/components/ConsoleAPI.js
@BINPATH@/components/ContactManager.js
@BINPATH@/components/ContactManager.manifest
@BINPATH@/components/PhoneNumberService.js
@BINPATH@/components/PhoneNumberService.manifest
@BINPATH@/components/SettingsManager.js
@BINPATH@/components/SettingsManager.manifest
@BINPATH@/components/SettingsService.js