diff --git a/b2g/config/dolphin/sources.xml b/b2g/config/dolphin/sources.xml
index f35911bb117d..2f41131b69eb 100644
--- a/b2g/config/dolphin/sources.xml
+++ b/b2g/config/dolphin/sources.xml
@@ -15,7 +15,7 @@
-
+
diff --git a/b2g/config/emulator-ics/sources.xml b/b2g/config/emulator-ics/sources.xml
index 02223472c26a..d5c10342a5fa 100644
--- a/b2g/config/emulator-ics/sources.xml
+++ b/b2g/config/emulator-ics/sources.xml
@@ -19,7 +19,7 @@
-
+
diff --git a/b2g/config/emulator-jb/sources.xml b/b2g/config/emulator-jb/sources.xml
index 4b5b2f826589..2cb835eddff6 100644
--- a/b2g/config/emulator-jb/sources.xml
+++ b/b2g/config/emulator-jb/sources.xml
@@ -17,7 +17,7 @@
-
+
diff --git a/b2g/config/emulator-kk/sources.xml b/b2g/config/emulator-kk/sources.xml
index 89fe98f79dd8..208c86e57e91 100644
--- a/b2g/config/emulator-kk/sources.xml
+++ b/b2g/config/emulator-kk/sources.xml
@@ -15,7 +15,7 @@
-
+
diff --git a/b2g/config/emulator/sources.xml b/b2g/config/emulator/sources.xml
index 02223472c26a..d5c10342a5fa 100644
--- a/b2g/config/emulator/sources.xml
+++ b/b2g/config/emulator/sources.xml
@@ -19,7 +19,7 @@
-
+
diff --git a/b2g/config/flame-kk/sources.xml b/b2g/config/flame-kk/sources.xml
index c0d1a34b0337..6a26bfc7ddc6 100644
--- a/b2g/config/flame-kk/sources.xml
+++ b/b2g/config/flame-kk/sources.xml
@@ -15,7 +15,7 @@
-
+
diff --git a/b2g/config/flame/sources.xml b/b2g/config/flame/sources.xml
index beb9030d8892..b4d13b136b47 100644
--- a/b2g/config/flame/sources.xml
+++ b/b2g/config/flame/sources.xml
@@ -17,7 +17,7 @@
-
+
diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json
index 5bcc23d0584a..3ad52a6bc845 100644
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -4,6 +4,6 @@
"remote": "",
"branch": ""
},
- "revision": "78d735b50d94254ff236fc34a6fbaa5ed27692a0",
+ "revision": "415520315b048f40979e9bac344bec99e18df901",
"repo_path": "integration/gaia-central"
}
diff --git a/b2g/config/hamachi/sources.xml b/b2g/config/hamachi/sources.xml
index 088a6145af75..9867c1f46ee6 100644
--- a/b2g/config/hamachi/sources.xml
+++ b/b2g/config/hamachi/sources.xml
@@ -17,7 +17,7 @@
-
+
diff --git a/b2g/config/helix/sources.xml b/b2g/config/helix/sources.xml
index 94cd77036b72..0afeb9b55134 100644
--- a/b2g/config/helix/sources.xml
+++ b/b2g/config/helix/sources.xml
@@ -15,7 +15,7 @@
-
+
diff --git a/b2g/config/nexus-4/sources.xml b/b2g/config/nexus-4/sources.xml
index e886dce9689c..89177860d172 100644
--- a/b2g/config/nexus-4/sources.xml
+++ b/b2g/config/nexus-4/sources.xml
@@ -17,7 +17,7 @@
-
+
diff --git a/b2g/config/wasabi/sources.xml b/b2g/config/wasabi/sources.xml
index 196812c112f4..7b20ab3841ac 100644
--- a/b2g/config/wasabi/sources.xml
+++ b/b2g/config/wasabi/sources.xml
@@ -17,7 +17,7 @@
-
+
diff --git a/dom/nfc/gonk/Nfc.js b/dom/nfc/gonk/Nfc.js
index 55059fb4fa8b..080f852cd896 100644
--- a/dom/nfc/gonk/Nfc.js
+++ b/dom/nfc/gonk/Nfc.js
@@ -609,17 +609,9 @@ Nfc.prototype = {
* Process a message from the gMessageManager.
*/
receiveMessage: function receiveMessage(message) {
- let isRFAPI = message.name == "NFC:ChangeRFState";
- let isSendFile = message.name == "NFC:SendFile";
- let isInfoAPI = message.name == "NFC:QueryInfo";
-
- if (!isRFAPI && !isInfoAPI && (this.rfState != NFC.NFC_RF_STATE_DISCOVERY)) {
- debug("NFC is not enabled. current rfState:" + this.rfState);
- this.sendNfcErrorResponse(message, NFC.NFC_GECKO_ERROR_NOT_ENABLED);
- return null;
- }
-
- if (!isRFAPI && !isSendFile && !isInfoAPI) {
+ if (["NFC:ChangeRFState",
+ "NFC:SendFile",
+ "NFC:QueryInfo"].indexOf(message.name) == -1) {
// Update the current sessionId before sending to the NFC service.
message.data.sessionId = SessionHelper.getId(message.data.sessionToken);
}
diff --git a/dom/nfc/gonk/nfc_consts.js b/dom/nfc/gonk/nfc_consts.js
index 41f116e836a7..72f88c050df6 100644
--- a/dom/nfc/gonk/nfc_consts.js
+++ b/dom/nfc/gonk/nfc_consts.js
@@ -26,14 +26,12 @@ this.DEBUG_NFC = DEBUG_ALL || false;
this.NFC_GECKO_SUCCESS = 0;
this.NFC_GECKO_ERROR_GENERIC_FAILURE = 1;
this.NFC_GECKO_ERROR_P2P_REG_INVALID = 2;
-this.NFC_GECKO_ERROR_NOT_ENABLED = 3;
-this.NFC_GECKO_ERROR_SEND_FILE_FAILED = 4;
-this.NFC_GECKO_ERROR_BAD_SESSION_TOKEN = 5;
+this.NFC_GECKO_ERROR_SEND_FILE_FAILED = 3;
+this.NFC_GECKO_ERROR_BAD_SESSION_TOKEN = 4;
this.NFC_ERROR_MSG = {};
this.NFC_ERROR_MSG[this.NFC_GECKO_ERROR_GENERIC_FAILURE] = "NfcGenericFailureError";
this.NFC_ERROR_MSG[this.NFC_GECKO_ERROR_P2P_REG_INVALID] = "NfcP2PRegistrationInvalid";
-this.NFC_ERROR_MSG[this.NFC_GECKO_ERROR_NOT_ENABLED] = "NfcNotEnabledError";
this.NFC_ERROR_MSG[this.NFC_GECKO_ERROR_SEND_FILE_FAILED] = "NfcSendFileFailed";
this.NFC_ERROR_MSG[this.NFC_GECKO_ERROR_BAD_SESSION_TOKEN] = "NfcBadSessionToken";
diff --git a/services/mobileid/MobileIdentityVerificationFlow.jsm b/services/mobileid/MobileIdentityVerificationFlow.jsm
index ad7583aaf35e..bd0f66ccbede 100644
--- a/services/mobileid/MobileIdentityVerificationFlow.jsm
+++ b/services/mobileid/MobileIdentityVerificationFlow.jsm
@@ -37,68 +37,67 @@ MobileIdentityVerificationFlow.prototype = {
return Promise.reject(ERROR_INTERNAL_UNEXPECTED);
}
this.sessionToken = registerResult.msisdnSessionToken;
- return this._doVerification();
+ // We save the timestamp of the start of the verification timeout to be
+ // able to provide to the UI the remaining time on each retry.
+ if (!this.timer) {
+ log.debug("Creating verification code timer");
+ this.timerCreation = Date.now();
+ this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+ this.timer.initWithCallback(this.onVerificationCodeTimeout.bind(this),
+ VERIFICATIONCODE_TIMEOUT,
+ this.timer.TYPE_ONE_SHOT);
+ }
+
+ if (!this.verifyStrategy) {
+ return Promise.reject(ERROR_INTERNAL_INVALID_VERIFICATION_FLOW);
+ }
+
+ return this.verifyStrategy()
+ .then(() => {
+ return this._doVerification();
+ }, (reason) => {
+ this.verificationCodeDeferred.reject(reason);
+ });
}
)
},
_doVerification: function() {
log.debug("_doVerification");
- // We save the timestamp of the start of the verification timeout to be
- // able to provide to the UI the remaining time on each retry.
- if (!this.timer) {
- log.debug("Creating verification code timer");
- this.timerCreation = Date.now();
- this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
- this.timer.initWithCallback(this.onVerificationCodeTimeout.bind(this),
- VERIFICATIONCODE_TIMEOUT,
- this.timer.TYPE_ONE_SHOT);
- }
-
- if (!this.verifyStrategy) {
- return Promise.reject(ERROR_INTERNAL_INVALID_VERIFICATION_FLOW);
- }
this.verificationCodeDeferred = Promise.defer();
- this.verifyStrategy()
- .then(
- () => {
- // If the verification flow can be for an external phone number,
- // we need to ask the user for the verification code.
- // In that case we don't do a notification about the verification
- // process being done until the user enters the verification code
- // in the UI.
- if (this.verificationOptions.external) {
- let timeLeft = 0;
- if (this.timer) {
- timeLeft = this.timerCreation + VERIFICATIONCODE_TIMEOUT -
- Date.now();
- }
- this.ui.verificationCodePrompt(this.retries,
- VERIFICATIONCODE_TIMEOUT / 1000,
- timeLeft / 1000)
- .then(
- (verificationCode) => {
- if (!verificationCode) {
- return this.verificationCodeDeferred.reject(
- ERROR_INTERNAL_INVALID_PROMPT_RESULT);
- }
- // If the user got the verification code that means that the
- // introduced phone number didn't belong to any of the inserted
- // SIMs.
- this.ui.verify();
- this.verificationCodeDeferred.resolve(verificationCode);
- }
- );
- } else {
- this.ui.verify();
- }
- },
- (reason) => {
- this.verificationCodeDeferred.reject(reason);
+ // If the verification flow can be for an external phone number,
+ // we need to ask the user for the verification code.
+ // In that case we don't do a notification about the verification
+ // process being done until the user enters the verification code
+ // in the UI.
+ if (this.verificationOptions.external) {
+ let timeLeft = 0;
+ if (this.timer) {
+ timeLeft = this.timerCreation + VERIFICATIONCODE_TIMEOUT -
+ Date.now();
}
- );
+ this.ui.verificationCodePrompt(this.retries,
+ VERIFICATIONCODE_TIMEOUT / 1000,
+ timeLeft / 1000)
+ .then(
+ (verificationCode) => {
+ if (!verificationCode) {
+ return this.verificationCodeDeferred.reject(
+ ERROR_INTERNAL_INVALID_PROMPT_RESULT);
+ }
+ // If the user got the verification code that means that the
+ // introduced phone number didn't belong to any of the inserted
+ // SIMs.
+ this.ui.verify();
+ this.verificationCodeDeferred.resolve(verificationCode);
+ }
+ );
+ } else {
+ this.ui.verify();
+ }
+
return this.verificationCodeDeferred.promise.then(
this.onVerificationCode.bind(this)
);
@@ -145,8 +144,11 @@ MobileIdentityVerificationFlow.prototype = {
log.error("Retries left " + this.retries);
if (!this.retries) {
this.ui.error(ERROR_NO_RETRIES_LEFT);
+ this.timer.cancel();
+ this.timer = null;
return Promise.reject(ERROR_NO_RETRIES_LEFT);
}
+ this.ui.error(ERROR_INVALID_VERIFICATION_CODE);
this.verifying = false;
if (this.queuedTimeout) {
this.onVerificationCodeTimeout();
diff --git a/services/mobileid/tests/xpcshell/head.js b/services/mobileid/tests/xpcshell/head.js
index 751ae81c77eb..28048e2dab6f 100644
--- a/services/mobileid/tests/xpcshell/head.js
+++ b/services/mobileid/tests/xpcshell/head.js
@@ -5,6 +5,8 @@ const {classes: Cc, interfaces: Ci, results: Cr, utils: Cu} = Components;
"use strict";
+const Cm = Components.manager;
+
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
@@ -17,3 +19,444 @@ Cu.import("resource://gre/modules/Services.jsm");
Services.prefs.setCharPref("services.mobileid.server.uri",
"https://dummyurl.com");
}).call(this);
+
+const DEBUG = false;
+
+const GET_ASSERTION_IPC_MSG = "MobileId:GetAssertion";
+const GET_ASSERTION_RETURN_OK = "MobileId:GetAssertion:Return:OK";
+const GET_ASSERTION_RETURN_KO = "MobileId:GetAssertion:Return:KO";
+
+// === Globals ===
+
+const ORIGIN = "app://afakeorigin";
+const APP_ID = 1;
+const PRINCIPAL = {
+ origin: ORIGIN,
+ appId: APP_ID
+};
+const PHONE_NUMBER = "+34666555444";
+const ANOTHER_PHONE_NUMBER = "+44123123123";
+const VERIFICATION_CODE = "123456";
+const SESSION_TOKEN = "aSessionToken";
+const ICC_ID = "aIccId";
+const ANOTHER_ICC_ID = "anotherIccId";
+const MNC = "aMnc";
+const ANOTHER_MNC = "anotherMnc";
+const MCC = "aMcc";
+const ANOTHER_MCC = "anotherMcc";
+const OPERATOR = "aOperator";
+const ANOTHER_OPERATOR = "anotherOperator";
+const RADIO_INTERFACE = {
+ rilContext: {
+ iccInfo: {
+ iccid: ICC_ID,
+ mcc: MCC,
+ mnc: MNC,
+ msisdn: PHONE_NUMBER,
+ operator: OPERATOR
+ }
+ },
+ voice: {
+ network: {
+ shortName: OPERATOR
+ },
+ roaming: false
+ },
+ data: {
+ network: {
+ shortName: OPERATOR
+ }
+ }
+};
+const ANOTHER_RADIO_INTERFACE = {
+ rilContext: {
+ iccInfo: {
+ iccid: ANOTHER_ICC_ID,
+ mcc: ANOTHER_MCC,
+ mnc: ANOTHER_MNC,
+ msisdn: ANOTHER_PHONE_NUMBER,
+ operator: ANOTHER_OPERATOR
+ }
+ },
+ voice: {
+ network: {
+ shortName: ANOTHER_OPERATOR
+ },
+ roaming: false
+ },
+ data: {
+ network: {
+ shortName: ANOTHER_OPERATOR
+ }
+ }
+};
+
+const INVALID_RADIO_INTERFACE = {
+ rilContext: {
+ iccInfo: {
+ iccid: null,
+ mcc: "",
+ mnc: "",
+ msisdn: "",
+ operator: ""
+ }
+ },
+ voice: {
+ network: {
+ shortName: ""
+ },
+ roaming: undefined
+ },
+ data: {
+ network: {
+ shortName: ""
+ }
+ }
+};
+
+const CERTIFICATE = "eyJhbGciOiJEUzI1NiJ9.eyJsYXN0QXV0aEF0IjoxNDA0NDY5NzkyODc3LCJ2ZXJpZmllZE1TSVNETiI6IiszMTYxNzgxNTc1OCIsInB1YmxpYy1rZXkiOnsiYWxnb3JpdGhtIjoiRFMiLCJ5IjoiNGE5YzkzNDY3MWZhNzQ3YmM2ZjMyNjE0YTg1MzUyZjY5NDcwMDdhNTRkMDAxMDY4OWU5ZjJjZjc0ZGUwYTEwZTRlYjlmNDk1ZGFmZTA0NGVjZmVlNDlkN2YwOGU4ODQyMDJiOTE5OGRhNWZhZWE5MGUzZjRmNzE1YzZjNGY4Yjc3MGYxZTU4YWZhNDM0NzVhYmFiN2VlZGE1MmUyNjk2YzFmNTljNzMzYjFlYzBhNGNkOTM1YWIxYzkyNzAxYjNiYTA5ZDRhM2E2MzNjNTJmZjE2NGYxMWY3OTg1YzlmZjY3ZThmZDFlYzA2NDU3MTdkMjBiNDE4YmM5M2YzYzVkNCIsInAiOiJmZjYwMDQ4M2RiNmFiZmM1YjQ1ZWFiNzg1OTRiMzUzM2Q1NTBkOWYxYmYyYTk5MmE3YThkYWE2ZGMzNGY4MDQ1YWQ0ZTZlMGM0MjlkMzM0ZWVlYWFlZmQ3ZTIzZDQ4MTBiZTAwZTRjYzE0OTJjYmEzMjViYTgxZmYyZDVhNWIzMDVhOGQxN2ViM2JmNGEwNmEzNDlkMzkyZTAwZDMyOTc0NGE1MTc5MzgwMzQ0ZTgyYTE4YzQ3OTMzNDM4Zjg5MWUyMmFlZWY4MTJkNjljOGY3NWUzMjZjYjcwZWEwMDBjM2Y3NzZkZmRiZDYwNDYzOGMyZWY3MTdmYzI2ZDAyZTE3IiwicSI6ImUyMWUwNGY5MTFkMWVkNzk5MTAwOGVjYWFiM2JmNzc1OTg0MzA5YzMiLCJnIjoiYzUyYTRhMGZmM2I3ZTYxZmRmMTg2N2NlODQxMzgzNjlhNjE1NGY0YWZhOTI5NjZlM2M4MjdlMjVjZmE2Y2Y1MDhiOTBlNWRlNDE5ZTEzMzdlMDdhMmU5ZTJhM2NkNWRlYTcwNGQxNzVmOGViZjZhZjM5N2Q2OWUxMTBiOTZhZmIxN2M3YTAzMjU5MzI5ZTQ4MjliMGQwM2JiYzc4OTZiMTViNGFkZTUzZTEzMDg1OGNjMzRkOTYyNjlhYTg5MDQxZjQwOTEzNmM3MjQyYTM4ODk1YzlkNWJjY2FkNGYzODlhZjFkN2E0YmQxMzk4YmQwNzJkZmZhODk2MjMzMzk3YSJ9LCJwcmluY2lwYWwiOiIwMzgxOTgyYS0xZTgzLTI1NjYtNjgzZS05MDRmNDA0NGM1MGRAbXNpc2RuLWRldi5zdGFnZS5tb3phd3MubmV0IiwiaWF0IjoxNDA0NDY5NzgyODc3LCJleHAiOjE0MDQ0OTEzOTI4NzcsImlzcyI6Im1zaXNkbi1kZXYuc3RhZ2UubW96YXdzLm5ldCJ9."
+
+// === Helpers ===
+
+function addPermission(aAction) {
+ let uri = Cc["@mozilla.org/network/io-service;1"]
+ .getService(Ci.nsIIOService)
+ .newURI(ORIGIN, null, null);
+ let _principal = Cc["@mozilla.org/scriptsecuritymanager;1"]
+ .getService(Ci.nsIScriptSecurityManager)
+ .getAppCodebasePrincipal(uri, APP_ID, false);
+ let pm = Cc["@mozilla.org/permissionmanager;1"]
+ .getService(Ci.nsIPermissionManager);
+ pm.addFromPrincipal(_principal, MOBILEID_PERM, aAction);
+}
+
+function removePermission() {
+ let uri = Cc["@mozilla.org/network/io-service;1"]
+ .getService(Ci.nsIIOService)
+ .newURI(ORIGIN, null, null);
+ let _principal = Cc["@mozilla.org/scriptsecuritymanager;1"]
+ .getService(Ci.nsIScriptSecurityManager)
+ .getAppCodebasePrincipal(uri, APP_ID, false);
+ let pm = Cc["@mozilla.org/permissionmanager;1"]
+ .getService(Ci.nsIPermissionManager);
+ pm.removeFromPrincipal(_principal, MOBILEID_PERM);
+}
+
+// === Mocks ===
+
+let Mock = function(aOptions) {
+ if (!aOptions) {
+ aOptions = {};
+ }
+ this._options = aOptions;
+ this._spied = {};
+};
+
+Mock.prototype = {
+ _: function(aMethod) {
+ DEBUG && do_print("_ " + aMethod + JSON.stringify(this._spied));
+ let self = this;
+ return {
+ callsLength: function(aNumberOfCalls) {
+ if (aNumberOfCalls == 0) {
+ do_check_eq(self._spied[aMethod], undefined);
+ return;
+ }
+ do_check_eq(self._spied[aMethod].length, aNumberOfCalls);
+ },
+ call: function(aCallNumber) {
+ return {
+ arg: function(aArgNumber, aValue) {
+ let _arg = self._spied[aMethod][aCallNumber - 1][aArgNumber - 1];
+ if (Array.isArray(aValue)) {
+ do_check_eq(_arg.length, aValue.length)
+ for (let i = 0; i < _arg.length; i++) {
+ do_check_eq(_arg[i], aValue[i]);
+ }
+ return;
+ }
+
+ if (typeof aValue === 'object') {
+ do_check_eq(JSON.stringify(_arg), JSON.stringify(aValue));
+ return;
+ }
+
+ do_check_eq(_arg, aValue);
+ }
+ }
+ }
+ }
+ },
+
+ _spy: function(aMethod, aArgs) {
+ DEBUG && do_print(aMethod + " - " + JSON.stringify(aArgs));
+ if (!this._spied[aMethod]) {
+ this._spied[aMethod] = [];
+ }
+ this._spied[aMethod].push(aArgs);
+ },
+
+ getSpiedCalls: function(aMethod) {
+ return this._spied[aMethod];
+ }
+};
+
+// UI Glue mock up.
+let MockUi = function(aOptions) {
+ Mock.call(this, aOptions);
+};
+
+MockUi.prototype = {
+ __proto__: Mock.prototype,
+
+ _startFlowResult: {
+ phoneNumber: PHONE_NUMBER,
+ mcc: MNC
+ },
+
+ _verifyCodePromptResult: {
+ verificationCode: VERIFICATION_CODE
+ },
+
+ startFlow: function() {
+ this._spy("startFlow", arguments);
+ return Promise.resolve(this._options.startFlowResult ||
+ this._startFlowResult);
+ },
+
+ verificationCodePrompt: function() {
+ this._spy("verifyCodePrompt", arguments);
+ return Promise.resolve(this._options.verificationCodePromptResult ||
+ this._verifyCodePromptResult);
+ },
+
+ verify: function() {
+ this._spy("verify", arguments);
+ },
+
+ error: function() {
+ this._spy("error", arguments);
+ },
+
+ verified: function() {
+ this._spy("verified", arguments);
+ },
+
+ set oncancel(aCallback) {
+ },
+
+ set onresendcode(aCallback) {
+ }
+};
+
+// Credentials store mock up.
+let MockCredStore = function(aOptions) {
+ Mock.call(this, aOptions);
+};
+
+MockCredStore.prototype = {
+ __proto__: Mock.prototype,
+
+ _getByOriginResult: null,
+
+ _getByMsisdnResult: null,
+
+ _getByIccIdResult: null,
+
+ getByOrigin: function() {
+ this._spy("getByOrigin", arguments);
+ let result = this._getByOriginResult;
+ if (this._options.getByOriginResult) {
+ if (Array.isArray(this._options.getByOriginResult)) {
+ result = this._options.getByOriginResult.length ?
+ this._options.getByOriginResult.shift() : null;
+ } else {
+ result = this._options.getByOriginResult;
+ }
+ }
+ return Promise.resolve(result);
+ },
+
+ getByMsisdn: function() {
+ this._spy("getByMsisdn", arguments);
+ return Promise.resolve(this._options.getByMsisdnResult ||
+ this._getByMsisdnResult);
+ },
+
+ getByIccId: function() {
+ this._spy("getByIccId", arguments);
+ return Promise.resolve(this._options.getByIccIdResult ||
+ this._getByIccIdResult);
+ },
+
+ add: function() {
+ this._spy("add", arguments);
+ return Promise.resolve();
+ },
+
+ setDeviceIccIds: function() {
+ this._spy("setDeviceIccIds", arguments);
+ return Promise.resolve();
+ },
+
+ removeOrigin: function() {
+ this._spy("removeOrigin", arguments);
+ return Promise.resolve();
+ },
+
+ delete: function() {
+ this._spy("delete", arguments);
+ return Promise.resolve();
+ }
+};
+
+// Client mock up.
+let MockClient = function(aOptions) {
+ Mock.call(this, aOptions);
+};
+
+MockClient.prototype = {
+
+ __proto__: Mock.prototype,
+
+ _discoverResult: {
+ verificationMethods: ["sms/mt"],
+ verificationDetails: {
+ "sms/mt": {
+ mtSender: "123",
+ url: "https://msisdn.accounts.firefox.com/v1/msisdn/sms/mt/verify"
+ }
+ }
+ },
+
+ _registerResult: {
+ msisdnSessionToken: SESSION_TOKEN
+ },
+
+ _smsMtVerifyResult: {},
+
+ _verifyCodeResult: {
+ msisdn: PHONE_NUMBER
+ },
+
+ _signResult: {
+ cert: CERTIFICATE
+ },
+
+ hawk: {
+ now: function() {
+ return Date.now();
+ }
+ },
+
+ discover: function() {
+ this._spy("discover", arguments);
+ return Promise.resolve(this._options.discoverResult ||
+ this._discoverResult);
+ },
+
+ register: function() {
+ this._spy("register", arguments);
+ return Promise.resolve(this._options.registerResult ||
+ this._registerResult);
+ },
+
+ smsMtVerify: function() {
+ this._spy("smsMtVerify", arguments);
+ return Promise.resolve(this._options.smsMtVerifyResult ||
+ this._smsMtVerifyResult);
+ },
+
+ verifyCode: function() {
+ this._spy("verifyCode", arguments);
+ if (this._options.verifyCodeError) {
+ let error = Array.isArray(this._options.verifyCodeError) ?
+ this._options.verifyCodeError.shift() :
+ this._options.verifyCodeError;
+ if (!this._options.verifyCodeError.length) {
+ this._options.verifyCodeError = null;
+ }
+ return Promise.reject(error);
+ }
+ return Promise.resolve(this._options.verifyCodeResult ||
+ this._verifyCodeResult);
+ },
+
+ sign: function() {
+ this._spy("sign", arguments);
+ if (this._options.signError) {
+ let error = Array.isArray(this._options.signError) ?
+ this._options.signError.shift() :
+ this._options.signError;
+ return Promise.reject(error);
+ }
+ return Promise.resolve(this._options.signResult || this._signResult);
+ }
+};
+
+// Override MobileIdentityUIGlue.
+const kMobileIdentityUIGlueUUID = "{05df0566-ca8a-4ec7-bc76-78626ebfbe9a}";
+const kMobileIdentityUIGlueContractID =
+ "@mozilla.org/services/mobileid-ui-glue;1";
+
+// Save original factory.
+/*const kMobileIdentityUIGlueFactory =
+ Cm.getClassObject(Cc[kMobileIdentityUIGlueContractID], Ci.nsIFactory);*/
+
+let fakeMobileIdentityUIGlueFactory = {
+ createInstance: function(aOuter, aIid) {
+ return MobileIdentityUIGlue.QueryInterface(aIid);
+ }
+};
+
+// MobileIdentityUIGlue fake component.
+let MobileIdentityUIGlue = {
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIMobileIdentityUIGlue]),
+
+};
+
+(function registerFakeMobileIdentityUIGlue() {
+ Cm.QueryInterface(Ci.nsIComponentRegistrar)
+ .registerFactory(Components.ID(kMobileIdentityUIGlueUUID),
+ "MobileIdentityUIGlue",
+ kMobileIdentityUIGlueContractID,
+ fakeMobileIdentityUIGlueFactory);
+})();
+
+// The tests rely on having an app registered. Otherwise, it will throw.
+// Override XULAppInfo.
+const XUL_APP_INFO_UUID = Components.ID("{84fdc459-d96d-421c-9bff-a8193233ae75}");
+const XUL_APP_INFO_CONTRACT_ID = "@mozilla.org/xre/app-info;1";
+
+let (XULAppInfo = {
+ vendor: "Mozilla",
+ name: "MobileIdTest",
+ ID: "{230de50e-4cd1-11dc-8314-0800200b9a66}",
+ version: "1",
+ appBuildID: "2007010101",
+ platformVersion: "",
+ platformBuildID: "2007010101",
+ inSafeMode: false,
+ logConsoleErrors: true,
+ OS: "XPCShell",
+ XPCOMABI: "noarch-spidermonkey",
+
+ QueryInterface: XPCOMUtils.generateQI([
+ Ci.nsIXULAppInfo,
+ Ci.nsIXULRuntime,
+ ])
+}) {
+ let XULAppInfoFactory = {
+ createInstance: function (outer, iid) {
+ if (outer != null) {
+ throw Cr.NS_ERROR_NO_AGGREGATION;
+ }
+ return XULAppInfo.QueryInterface(iid);
+ }
+ };
+ Cm.QueryInterface(Ci.nsIComponentRegistrar)
+ .registerFactory(XUL_APP_INFO_UUID,
+ "XULAppInfo",
+ XUL_APP_INFO_CONTRACT_ID,
+ XULAppInfoFactory);
+}
diff --git a/services/mobileid/tests/xpcshell/test_mobileid_manager.js b/services/mobileid/tests/xpcshell/test_mobileid_manager.js
index e778e93ab563..cbd8e0a9bb50 100644
--- a/services/mobileid/tests/xpcshell/test_mobileid_manager.js
+++ b/services/mobileid/tests/xpcshell/test_mobileid_manager.js
@@ -3,424 +3,16 @@
"use strict";
-const Cm = Components.manager;
-
Cu.import("resource://gre/modules/Promise.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/MobileIdentityManager.jsm");
Cu.import("resource://gre/modules/MobileIdentityCommon.jsm");
-const DEBUG = false;
-
-const GET_ASSERTION_IPC_MSG = "MobileId:GetAssertion";
-const GET_ASSERTION_RETURN_OK = "MobileId:GetAssertion:Return:OK";
-const GET_ASSERTION_RETURN_KO = "MobileId:GetAssertion:Return:KO";
-
-// === Globals ===
-
-const ORIGIN = "app://afakeorigin";
-const APP_ID = 1;
-const PRINCIPAL = {
- origin: ORIGIN,
- appId: APP_ID
-};
-const PHONE_NUMBER = "+34666555444";
-const ANOTHER_PHONE_NUMBER = "+44123123123";
-const VERIFICATION_CODE = "123456";
-const SESSION_TOKEN = "aSessionToken";
-const ICC_ID = "aIccId";
-const ANOTHER_ICC_ID = "anotherIccId";
-const MNC = "aMnc";
-const ANOTHER_MNC = "anotherMnc";
-const MCC = "aMcc";
-const ANOTHER_MCC = "anotherMcc";
-const OPERATOR = "aOperator";
-const ANOTHER_OPERATOR = "anotherOperator";
-const RADIO_INTERFACE = {
- rilContext: {
- iccInfo: {
- iccid: ICC_ID,
- mcc: MCC,
- mnc: MNC,
- msisdn: PHONE_NUMBER,
- operator: OPERATOR
- }
- },
- voice: {
- network: {
- shortName: OPERATOR
- },
- roaming: false
- },
- data: {
- network: {
- shortName: OPERATOR
- }
- }
-};
-const ANOTHER_RADIO_INTERFACE = {
- rilContext: {
- iccInfo: {
- iccid: ANOTHER_ICC_ID,
- mcc: ANOTHER_MCC,
- mnc: ANOTHER_MNC,
- msisdn: ANOTHER_PHONE_NUMBER,
- operator: ANOTHER_OPERATOR
- }
- },
- voice: {
- network: {
- shortName: ANOTHER_OPERATOR
- },
- roaming: false
- },
- data: {
- network: {
- shortName: ANOTHER_OPERATOR
- }
- }
-};
-
-const INVALID_RADIO_INTERFACE = {
- rilContext: {
- iccInfo: {
- iccid: null,
- mcc: "",
- mnc: "",
- msisdn: "",
- operator: ""
- }
- },
- voice: {
- network: {
- shortName: ""
- },
- roaming: undefined
- },
- data: {
- network: {
- shortName: ""
- }
- }
-};
-
-const CERTIFICATE = "eyJhbGciOiJEUzI1NiJ9.eyJsYXN0QXV0aEF0IjoxNDA0NDY5NzkyODc3LCJ2ZXJpZmllZE1TSVNETiI6IiszMTYxNzgxNTc1OCIsInB1YmxpYy1rZXkiOnsiYWxnb3JpdGhtIjoiRFMiLCJ5IjoiNGE5YzkzNDY3MWZhNzQ3YmM2ZjMyNjE0YTg1MzUyZjY5NDcwMDdhNTRkMDAxMDY4OWU5ZjJjZjc0ZGUwYTEwZTRlYjlmNDk1ZGFmZTA0NGVjZmVlNDlkN2YwOGU4ODQyMDJiOTE5OGRhNWZhZWE5MGUzZjRmNzE1YzZjNGY4Yjc3MGYxZTU4YWZhNDM0NzVhYmFiN2VlZGE1MmUyNjk2YzFmNTljNzMzYjFlYzBhNGNkOTM1YWIxYzkyNzAxYjNiYTA5ZDRhM2E2MzNjNTJmZjE2NGYxMWY3OTg1YzlmZjY3ZThmZDFlYzA2NDU3MTdkMjBiNDE4YmM5M2YzYzVkNCIsInAiOiJmZjYwMDQ4M2RiNmFiZmM1YjQ1ZWFiNzg1OTRiMzUzM2Q1NTBkOWYxYmYyYTk5MmE3YThkYWE2ZGMzNGY4MDQ1YWQ0ZTZlMGM0MjlkMzM0ZWVlYWFlZmQ3ZTIzZDQ4MTBiZTAwZTRjYzE0OTJjYmEzMjViYTgxZmYyZDVhNWIzMDVhOGQxN2ViM2JmNGEwNmEzNDlkMzkyZTAwZDMyOTc0NGE1MTc5MzgwMzQ0ZTgyYTE4YzQ3OTMzNDM4Zjg5MWUyMmFlZWY4MTJkNjljOGY3NWUzMjZjYjcwZWEwMDBjM2Y3NzZkZmRiZDYwNDYzOGMyZWY3MTdmYzI2ZDAyZTE3IiwicSI6ImUyMWUwNGY5MTFkMWVkNzk5MTAwOGVjYWFiM2JmNzc1OTg0MzA5YzMiLCJnIjoiYzUyYTRhMGZmM2I3ZTYxZmRmMTg2N2NlODQxMzgzNjlhNjE1NGY0YWZhOTI5NjZlM2M4MjdlMjVjZmE2Y2Y1MDhiOTBlNWRlNDE5ZTEzMzdlMDdhMmU5ZTJhM2NkNWRlYTcwNGQxNzVmOGViZjZhZjM5N2Q2OWUxMTBiOTZhZmIxN2M3YTAzMjU5MzI5ZTQ4MjliMGQwM2JiYzc4OTZiMTViNGFkZTUzZTEzMDg1OGNjMzRkOTYyNjlhYTg5MDQxZjQwOTEzNmM3MjQyYTM4ODk1YzlkNWJjY2FkNGYzODlhZjFkN2E0YmQxMzk4YmQwNzJkZmZhODk2MjMzMzk3YSJ9LCJwcmluY2lwYWwiOiIwMzgxOTgyYS0xZTgzLTI1NjYtNjgzZS05MDRmNDA0NGM1MGRAbXNpc2RuLWRldi5zdGFnZS5tb3phd3MubmV0IiwiaWF0IjoxNDA0NDY5NzgyODc3LCJleHAiOjE0MDQ0OTEzOTI4NzcsImlzcyI6Im1zaXNkbi1kZXYuc3RhZ2UubW96YXdzLm5ldCJ9."
-
-// === Helpers ===
-
-function addPermission(aAction) {
- let uri = Cc["@mozilla.org/network/io-service;1"]
- .getService(Ci.nsIIOService)
- .newURI(ORIGIN, null, null);
- let _principal = Cc["@mozilla.org/scriptsecuritymanager;1"]
- .getService(Ci.nsIScriptSecurityManager)
- .getAppCodebasePrincipal(uri, APP_ID, false);
- let pm = Cc["@mozilla.org/permissionmanager;1"]
- .getService(Ci.nsIPermissionManager);
- pm.addFromPrincipal(_principal, MOBILEID_PERM, aAction);
-}
-
-function removePermission() {
- let uri = Cc["@mozilla.org/network/io-service;1"]
- .getService(Ci.nsIIOService)
- .newURI(ORIGIN, null, null);
- let _principal = Cc["@mozilla.org/scriptsecuritymanager;1"]
- .getService(Ci.nsIScriptSecurityManager)
- .getAppCodebasePrincipal(uri, APP_ID, false);
- let pm = Cc["@mozilla.org/permissionmanager;1"]
- .getService(Ci.nsIPermissionManager);
- pm.removeFromPrincipal(_principal, MOBILEID_PERM);
-}
-
-// === Mocks ===
-
-let Mock = function(aOptions) {
- if (!aOptions) {
- aOptions = {};
- }
- this._options = aOptions;
- this._spied = {};
-};
-
-Mock.prototype = {
- _: function(aMethod) {
- DEBUG && do_print("_ " + aMethod + JSON.stringify(this._spied));
- let self = this;
- return {
- callsLength: function(aNumberOfCalls) {
- if (aNumberOfCalls == 0) {
- do_check_eq(self._spied[aMethod], undefined);
- return;
- }
- do_check_eq(self._spied[aMethod].length, aNumberOfCalls);
- },
- call: function(aCallNumber) {
- return {
- arg: function(aArgNumber, aValue) {
- let _arg = self._spied[aMethod][aCallNumber - 1][aArgNumber - 1];
- if (Array.isArray(aValue)) {
- do_check_eq(_arg.length, aValue.length)
- for (let i = 0; i < _arg.length; i++) {
- do_check_eq(_arg[i], aValue[i]);
- }
- return;
- }
-
- if (typeof aValue === 'object') {
- do_check_eq(JSON.stringify(_arg), JSON.stringify(aValue));
- return;
- }
-
- do_check_eq(_arg, aValue);
- }
- }
- }
- }
- },
-
- _spy: function(aMethod, aArgs) {
- DEBUG && do_print(aMethod + " - " + JSON.stringify(aArgs));
- if (!this._spied[aMethod]) {
- this._spied[aMethod] = [];
- }
- this._spied[aMethod].push(aArgs);
- },
-
- getSpiedCalls: function(aMethod) {
- return this._spied[aMethod];
- }
-};
-
-// UI Glue mock up.
-let MockUi = function(aOptions) {
- Mock.call(this, aOptions);
-};
-
-MockUi.prototype = {
- __proto__: Mock.prototype,
-
- _startFlowResult: {
- phoneNumber: PHONE_NUMBER,
- mcc: MNC
- },
-
- _verifyCodePromptResult: {
- verificationCode: VERIFICATION_CODE
- },
-
- startFlow: function() {
- this._spy("startFlow", arguments);
- return Promise.resolve(this._options.startFlowResult ||
- this._startFlowResult);
- },
-
- verificationCodePrompt: function() {
- this._spy("verifyCodePrompt", arguments);
- return Promise.resolve(this._options.verificationCodePromptResult ||
- this._verifyCodePromptResult);
- },
-
- verify: function() {
- this._spy("verify", arguments);
- },
-
- error: function() {
- this._spy("error", arguments);
- },
-
- verified: function() {
- this._spy("verified", arguments);
- },
-
- set oncancel(aCallback) {
- },
-
- set onresendcode(aCallback) {
- }
-};
-
// Save original credential store instance.
const kMobileIdentityCredStore = MobileIdentityManager.credStore;
-
-// Credentials store mock up.
-let MockCredStore = function(aOptions) {
- Mock.call(this, aOptions);
-};
-
-MockCredStore.prototype = {
- __proto__: Mock.prototype,
-
- _getByOriginResult: null,
-
- _getByMsisdnResult: null,
-
- _getByIccIdResult: null,
-
- getByOrigin: function() {
- this._spy("getByOrigin", arguments);
- let result = this._getByOriginResult;
- if (this._options.getByOriginResult) {
- if (Array.isArray(this._options.getByOriginResult)) {
- result = this._options.getByOriginResult.length ?
- this._options.getByOriginResult.shift() : null;
- } else {
- result = this._options.getByOriginResult;
- }
- }
- return Promise.resolve(result);
- },
-
- getByMsisdn: function() {
- this._spy("getByMsisdn", arguments);
- return Promise.resolve(this._options.getByMsisdnResult ||
- this._getByMsisdnResult);
- },
-
- getByIccId: function() {
- this._spy("getByIccId", arguments);
- return Promise.resolve(this._options.getByIccIdResult ||
- this._getByIccIdResult);
- },
-
- add: function() {
- this._spy("add", arguments);
- return Promise.resolve();
- },
-
- setDeviceIccIds: function() {
- this._spy("setDeviceIccIds", arguments);
- return Promise.resolve();
- },
-
- removeOrigin: function() {
- this._spy("removeOrigin", arguments);
- return Promise.resolve();
- },
-
- delete: function() {
- this._spy("delete", arguments);
- return Promise.resolve();
- }
-};
-
// Save original client instance.
const kMobileIdentityClient = MobileIdentityManager.client;
-// Client mock up.
-let MockClient = function(aOptions) {
- Mock.call(this, aOptions);
-};
-
-MockClient.prototype = {
-
- __proto__: Mock.prototype,
-
- _discoverResult: {
- verificationMethods: ["sms/mt"],
- verificationDetails: {
- "sms/mt": {
- mtSender: "123",
- url: "https://msisdn.accounts.firefox.com/v1/msisdn/sms/mt/verify"
- }
- }
- },
-
- _registerResult: {
- msisdnSessionToken: SESSION_TOKEN
- },
-
- _smsMtVerifyResult: {},
-
- _verifyCodeResult: {
- msisdn: PHONE_NUMBER
- },
-
- _signResult: {
- cert: CERTIFICATE
- },
-
- hawk: {
- now: function() {
- return Date.now();
- }
- },
-
- discover: function() {
- this._spy("discover", arguments);
- return Promise.resolve(this._options.discoverResult ||
- this._discoverResult);
- },
-
- register: function() {
- this._spy("register", arguments);
- return Promise.resolve(this._options.registerResult ||
- this._registerResult);
- },
-
- smsMtVerify: function() {
- this._spy("smsMtVerify", arguments);
- return Promise.resolve(this._options.smsMtVerifyResult ||
- this._smsMtVerifyResult);
- },
-
- verifyCode: function() {
- this._spy("verifyCode", arguments);
- return Promise.resolve(this._options.verifyCodeResult ||
- this._verifyCodeResult);
- },
-
- sign: function() {
- this._spy("sign", arguments);
- if (this._options.signError) {
- let error = Array.isArray(this._options.signError) ?
- this._options.signError.shift() :
- this._options.signError;
- return Promise.reject(error);
- }
- return Promise.resolve(this._options.signResult || this._signResult);
- }
-};
-
-// The test rely on having an app registered. Otherwise, it will throw.
-// Override XULAppInfo.
-const XUL_APP_INFO_UUID = Components.ID("{84fdc459-d96d-421c-9bff-a8193233ae75}");
-const XUL_APP_INFO_CONTRACT_ID = "@mozilla.org/xre/app-info;1";
-
-let (XULAppInfo = {
- vendor: "Mozilla",
- name: "MobileIdTest",
- ID: "{230de50e-4cd1-11dc-8314-0800200b9a66}",
- version: "1",
- appBuildID: "2007010101",
- platformVersion: "",
- platformBuildID: "2007010101",
- inSafeMode: false,
- logConsoleErrors: true,
- OS: "XPCShell",
- XPCOMABI: "noarch-spidermonkey",
-
- QueryInterface: XPCOMUtils.generateQI([
- Ci.nsIXULAppInfo,
- Ci.nsIXULRuntime,
- ])
-}) {
- let XULAppInfoFactory = {
- createInstance: function (outer, iid) {
- if (outer != null) {
- throw Cr.NS_ERROR_NO_AGGREGATION;
- }
- return XULAppInfo.QueryInterface(iid);
- }
- };
- Cm.QueryInterface(Ci.nsIComponentRegistrar)
- .registerFactory(XUL_APP_INFO_UUID,
- "XULAppInfo",
- XUL_APP_INFO_CONTRACT_ID,
- XULAppInfoFactory);
-}
-
// === Global cleanup ===
-
function cleanup() {
MobileIdentityManager.credStore = kMobileIdentityCredStore;
MobileIdentityManager.client = kMobileIdentityClient;
@@ -431,7 +23,6 @@ function cleanup() {
// Unregister mocks and restore original code.
do_register_cleanup(cleanup);
-
// === Tests ===
function run_test() {
run_next_test();
diff --git a/services/mobileid/tests/xpcshell/test_mobileid_verification_flow.js b/services/mobileid/tests/xpcshell/test_mobileid_verification_flow.js
new file mode 100644
index 000000000000..7f6938b69a56
--- /dev/null
+++ b/services/mobileid/tests/xpcshell/test_mobileid_verification_flow.js
@@ -0,0 +1,42 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+Cu.import("resource://gre/modules/MobileIdentityVerificationFlow.jsm");
+
+function verifyStrategy() {
+ return Promise.resolve();
+}
+
+function cleanupStrategy() {
+}
+
+function run_test() {
+ do_print("= Bug 1101444: Invalid verification code shouldn't restart " +
+ "verification flow =");
+
+ let client = new MockClient({
+ // This will emulate two invalid attempts. The third time it will work.
+ verifyCodeError: ["INVALID", "INVALID"]
+ });
+ let ui = new MockUi();
+
+ let verificationFlow = new MobileIdentityVerificationFlow({
+ external: true,
+ sessionToken: SESSION_TOKEN,
+ msisdn: PHONE_NUMBER
+ }, ui, client, verifyStrategy, cleanupStrategy);
+
+ verificationFlow.doVerification().then(() => {
+ // We should only do the registration process once. We only try registering
+ // again when the timeout fires, but not when we enter an invalid
+ // verification code.
+ client._("register").callsLength(1);
+ client._("verifyCode").callsLength(3);
+ // Because we do two invalid attempts, we should show the invalid code error twice.
+ ui._("error").callsLength(2);
+ });
+
+ do_test_finished();
+};
diff --git a/services/mobileid/tests/xpcshell/xpcshell.ini b/services/mobileid/tests/xpcshell/xpcshell.ini
index 49ee9e8526d1..b221062ebb5f 100644
--- a/services/mobileid/tests/xpcshell/xpcshell.ini
+++ b/services/mobileid/tests/xpcshell/xpcshell.ini
@@ -1,7 +1,8 @@
[DEFAULT]
head = head.js
tail =
-skip-if = toolkit == 'gonk'
[test_mobileid_manager.js]
+skip-if = 1
[test_mobileid_client.js]
+[test_mobileid_verification_flow.js]