michael@0: /* Any copyright is dedicated to the Public Domain. michael@0: * http://creativecommons.org/publicdomain/zero/1.0/ */ michael@0: michael@0: const {Cc: Cc, Ci: Ci, Cr: Cr, Cu: Cu} = SpecialPowers; michael@0: michael@0: let Promise = Cu.import("resource://gre/modules/Promise.jsm").Promise; michael@0: michael@0: /** michael@0: * Push required permissions and test if |navigator.mozMobileMessage| exists. michael@0: * Resolve if it does, reject otherwise. michael@0: * michael@0: * Fulfill params: michael@0: * manager -- an reference to navigator.mozMobileMessage. michael@0: * michael@0: * Reject params: (none) michael@0: * michael@0: * @return A deferred promise. michael@0: */ michael@0: let manager; michael@0: function ensureMobileMessage() { michael@0: let deferred = Promise.defer(); michael@0: michael@0: let permissions = [{ michael@0: "type": "sms", michael@0: "allow": 1, michael@0: "context": document, michael@0: }]; michael@0: SpecialPowers.pushPermissions(permissions, function() { michael@0: ok(true, "permissions pushed: " + JSON.stringify(permissions)); michael@0: michael@0: manager = window.navigator.mozMobileMessage; michael@0: if (manager) { michael@0: log("navigator.mozMobileMessage is instance of " + manager.constructor); michael@0: } else { michael@0: log("navigator.mozMobileMessage is undefined."); michael@0: } michael@0: michael@0: if (manager instanceof MozMobileMessageManager) { michael@0: deferred.resolve(manager); michael@0: } else { michael@0: deferred.reject(); michael@0: } michael@0: }); michael@0: michael@0: return deferred.promise; michael@0: } michael@0: michael@0: /** michael@0: * Wait for one named MobileMessageManager event. michael@0: * michael@0: * Resolve if that named event occurs. Never reject. michael@0: * michael@0: * Fulfill params: the DOMEvent passed. michael@0: * michael@0: * @param aEventName michael@0: * A string event name. michael@0: * michael@0: * @return A deferred promise. michael@0: */ michael@0: function waitForManagerEvent(aEventName) { michael@0: let deferred = Promise.defer(); michael@0: michael@0: manager.addEventListener(aEventName, function onevent(aEvent) { michael@0: manager.removeEventListener(aEventName, onevent); michael@0: michael@0: ok(true, "MobileMessageManager event '" + aEventName + "' got."); michael@0: deferred.resolve(aEvent); michael@0: }); michael@0: michael@0: return deferred.promise; michael@0: } michael@0: michael@0: /** michael@0: * Send a SMS message to a single receiver. Resolve if it succeeds, reject michael@0: * otherwise. michael@0: * michael@0: * Fulfill params: michael@0: * message -- the sent SmsMessage. michael@0: * michael@0: * Reject params: michael@0: * error -- a DOMError. michael@0: * michael@0: * @param aReceiver the address of the receiver. michael@0: * @param aText the text body of the message. michael@0: * michael@0: * @return A deferred promise. michael@0: */ michael@0: function sendSmsWithSuccess(aReceiver, aText) { michael@0: let deferred = Promise.defer(); michael@0: michael@0: let request = manager.send(aReceiver, aText); michael@0: request.onsuccess = function(event) { michael@0: deferred.resolve(event.target.result); michael@0: }; michael@0: request.onerror = function(event) { michael@0: deferred.reject(event.target.error); michael@0: }; michael@0: michael@0: return deferred.promise; michael@0: } michael@0: michael@0: /** michael@0: * Send a MMS message with specified parameters. Resolve if it fails, reject michael@0: * otherwise. michael@0: * michael@0: * Fulfill params: michael@0: * { michael@0: * message, -- the failed MmsMessage michael@0: * error, -- error of the send request michael@0: * } michael@0: * michael@0: * Reject params: (none) michael@0: * michael@0: * @param aMmsParameters a MmsParameters instance. michael@0: * michael@0: * @param aSendParameters a MmsSendParameters instance. michael@0: * michael@0: * @return A deferred promise. michael@0: */ michael@0: function sendMmsWithFailure(aMmsParameters, aSendParameters) { michael@0: let deferred = Promise.defer(); michael@0: michael@0: let result = { message: null, error: null }; michael@0: function got(which, value) { michael@0: result[which] = value; michael@0: if (result.message != null && result.error != null) { michael@0: deferred.resolve(result); michael@0: } michael@0: } michael@0: michael@0: manager.addEventListener("failed", function onfailed(event) { michael@0: manager.removeEventListener("failed", onfailed); michael@0: got("message", event.message); michael@0: }); michael@0: michael@0: let request = manager.sendMMS(aMmsParameters, aSendParameters); michael@0: request.onsuccess = function(event) { michael@0: deferred.reject(); michael@0: }; michael@0: request.onerror = function(event) { michael@0: got("error", event.target.error); michael@0: } michael@0: michael@0: return deferred.promise; michael@0: } michael@0: michael@0: /** michael@0: * Retrieve messages from database. michael@0: * michael@0: * Fulfill params: michael@0: * messages -- an array of {Sms,Mms}Message instances. michael@0: * michael@0: * Reject params: michael@0: * event -- a DOMEvent michael@0: * michael@0: * @param aFilter an optional MozSmsFilter instance. michael@0: * @param aReverse a boolean value indicating whether the order of the messages michael@0: * should be reversed. michael@0: * michael@0: * @return A deferred promise. michael@0: */ michael@0: function getMessages(aFilter, aReverse) { michael@0: let deferred = Promise.defer(); michael@0: michael@0: if (!aFilter) { michael@0: aFilter = new MozSmsFilter; michael@0: } michael@0: let messages = []; michael@0: let cursor = manager.getMessages(aFilter, aReverse || false); michael@0: cursor.onsuccess = function(aEvent) { michael@0: if (cursor.result) { michael@0: messages.push(cursor.result); michael@0: cursor.continue(); michael@0: return; michael@0: } michael@0: michael@0: deferred.resolve(messages); michael@0: }; michael@0: cursor.onerror = deferred.reject.bind(deferred); michael@0: michael@0: return deferred.promise; michael@0: } michael@0: michael@0: /** michael@0: * Retrieve all messages from database. michael@0: * michael@0: * Fulfill params: michael@0: * messages -- an array of {Sms,Mms}Message instances. michael@0: * michael@0: * Reject params: michael@0: * event -- a DOMEvent michael@0: * michael@0: * @return A deferred promise. michael@0: */ michael@0: function getAllMessages() { michael@0: return getMessages(null, false); michael@0: } michael@0: michael@0: /** michael@0: * Retrieve all threads from database. michael@0: * michael@0: * Fulfill params: michael@0: * threads -- an array of MozMobileMessageThread instances. michael@0: * michael@0: * Reject params: michael@0: * event -- a DOMEvent michael@0: * michael@0: * @return A deferred promise. michael@0: */ michael@0: function getAllThreads() { michael@0: let deferred = Promise.defer(); michael@0: michael@0: let threads = []; michael@0: let cursor = manager.getThreads(); michael@0: cursor.onsuccess = function(aEvent) { michael@0: if (cursor.result) { michael@0: threads.push(cursor.result); michael@0: cursor.continue(); michael@0: return; michael@0: } michael@0: michael@0: deferred.resolve(threads); michael@0: }; michael@0: cursor.onerror = deferred.reject.bind(deferred); michael@0: michael@0: return deferred.promise; michael@0: } michael@0: michael@0: /** michael@0: * Retrieve a single specified thread from database. michael@0: * michael@0: * Fulfill params: michael@0: * thread -- a MozMobileMessageThread instance. michael@0: * michael@0: * Reject params: michael@0: * event -- a DOMEvent if an error occurs in the retrieving process, or michael@0: * undefined if there's no such thread. michael@0: * michael@0: * @aThreadId a numeric value identifying the target thread. michael@0: * michael@0: * @return A deferred promise. michael@0: */ michael@0: function getThreadById(aThreadId) { michael@0: return getAllThreads() michael@0: .then(function(aThreads) { michael@0: for (let thread of aThreads) { michael@0: if (thread.id === aThreadId) { michael@0: return thread; michael@0: } michael@0: } michael@0: throw undefined; michael@0: }); michael@0: } michael@0: michael@0: /** michael@0: * Delete messages specified from database. michael@0: * michael@0: * Fulfill params: michael@0: * result -- an array of boolean values indicating whether delesion was michael@0: * actually performed on the message record with corresponding id. michael@0: * michael@0: * Reject params: michael@0: * event -- a DOMEvent. michael@0: * michael@0: * @aMessageId an array of numeric values identifying the target messages. michael@0: * michael@0: * @return An empty array if nothing to be deleted; otherwise, a deferred promise. michael@0: */ michael@0: function deleteMessagesById(aMessageIds) { michael@0: if (!aMessageIds.length) { michael@0: ok(true, "no message to be deleted"); michael@0: return []; michael@0: } michael@0: michael@0: let deferred = Promise.defer(); michael@0: michael@0: let request = manager.delete(aMessageIds); michael@0: request.onsuccess = function(event) { michael@0: deferred.resolve(event.target.result); michael@0: }; michael@0: request.onerror = deferred.reject.bind(deferred); michael@0: michael@0: return deferred.promise; michael@0: } michael@0: michael@0: /** michael@0: * Delete messages specified from database. michael@0: * michael@0: * Fulfill params: michael@0: * result -- an array of boolean values indicating whether delesion was michael@0: * actually performed on the message record with corresponding id. michael@0: * michael@0: * Reject params: michael@0: * event -- a DOMEvent. michael@0: * michael@0: * @aMessages an array of {Sms,Mms}Message instances. michael@0: * michael@0: * @return A deferred promise. michael@0: */ michael@0: function deleteMessages(aMessages) { michael@0: let ids = messagesToIds(aMessages); michael@0: return deleteMessagesById(ids); michael@0: } michael@0: michael@0: /** michael@0: * Delete all messages from database. michael@0: * michael@0: * Fulfill params: michael@0: * ids -- an array of numeric values identifying those deleted michael@0: * {Sms,Mms}Messages. michael@0: * michael@0: * Reject params: michael@0: * event -- a DOMEvent. michael@0: * michael@0: * @return A deferred promise. michael@0: */ michael@0: function deleteAllMessages() { michael@0: return getAllMessages().then(deleteMessages); michael@0: } michael@0: michael@0: let pendingEmulatorCmdCount = 0; michael@0: michael@0: /** michael@0: * Send emulator command with safe guard. michael@0: * michael@0: * We should only call |finish()| after all emulator command transactions michael@0: * end, so here comes with the pending counter. Resolve when the emulator michael@0: * gives positive response, and reject otherwise. michael@0: * michael@0: * Fulfill params: michael@0: * result -- an array of emulator response lines. michael@0: * michael@0: * Reject params: michael@0: * result -- an array of emulator response lines. michael@0: * michael@0: * @return A deferred promise. michael@0: */ michael@0: function runEmulatorCmdSafe(aCommand) { michael@0: let deferred = Promise.defer(); michael@0: michael@0: ++pendingEmulatorCmdCount; michael@0: runEmulatorCmd(aCommand, function(aResult) { michael@0: --pendingEmulatorCmdCount; michael@0: michael@0: ok(true, "Emulator response: " + JSON.stringify(aResult)); michael@0: if (Array.isArray(aResult) && aResult[0] === "OK") { michael@0: deferred.resolve(aResult); michael@0: } else { michael@0: deferred.reject(aResult); michael@0: } michael@0: }); michael@0: michael@0: return deferred.promise; michael@0: } michael@0: michael@0: /** michael@0: * Send simple text SMS to emulator. michael@0: * michael@0: * Fulfill params: michael@0: * result -- an array of emulator response lines. michael@0: * michael@0: * Reject params: michael@0: * result -- an array of emulator response lines. michael@0: * michael@0: * @return A deferred promise. michael@0: */ michael@0: function sendTextSmsToEmulator(aFrom, aText) { michael@0: let command = "sms send " + aFrom + " " + aText; michael@0: return runEmulatorCmdSafe(command); michael@0: } michael@0: michael@0: /** michael@0: * Send raw SMS TPDU to emulator. michael@0: * michael@0: * @param: aPdu michael@0: * A hex string representing the whole SMS T-PDU. michael@0: * michael@0: * Fulfill params: michael@0: * result -- an array of emulator response lines. michael@0: * michael@0: * Reject params: michael@0: * result -- an array of emulator response lines. michael@0: * michael@0: * @return A deferred promise. michael@0: */ michael@0: function sendRawSmsToEmulator(aPdu) { michael@0: let command = "sms pdu " + aPdu; michael@0: return runEmulatorCmdSafe(command); michael@0: } michael@0: michael@0: /** michael@0: * Send multiple raw SMS TPDU to emulator and wait michael@0: * michael@0: * @param: aPdus michael@0: * A array of hex strings. Each represents a SMS T-PDU. michael@0: * michael@0: * Fulfill params: michael@0: * result -- array of resolved Promise, where michael@0: * result[0].message representing the received message. michael@0: * result[1-n] represents the response of sent emulator command. michael@0: * michael@0: * Reject params: michael@0: * result -- an array of emulator response lines. michael@0: * michael@0: * @return A deferred promise. michael@0: */ michael@0: function sendMultipleRawSmsToEmulatorAndWait(aPdus) { michael@0: let promises = []; michael@0: michael@0: promises.push(waitForManagerEvent("received")); michael@0: for (let pdu of aPdus) { michael@0: promises.push(sendRawSmsToEmulator(pdu)); michael@0: } michael@0: michael@0: return Promise.all(promises); michael@0: } michael@0: michael@0: /** michael@0: * Create a new array of id attribute of input messages. michael@0: * michael@0: * @param aMessages an array of {Sms,Mms}Message instances. michael@0: * michael@0: * @return an array of numeric values. michael@0: */ michael@0: function messagesToIds(aMessages) { michael@0: let ids = []; michael@0: for (let message of aMessages) { michael@0: ids.push(message.id); michael@0: } michael@0: return ids; michael@0: } michael@0: michael@0: /** michael@0: * Flush permission settings and call |finish()|. michael@0: */ michael@0: function cleanUp() { michael@0: waitFor(function() { michael@0: SpecialPowers.flushPermissions(function() { michael@0: // Use ok here so that we have at least one test run. michael@0: ok(true, "permissions flushed"); michael@0: michael@0: finish(); michael@0: }); michael@0: }, function() { michael@0: return pendingEmulatorCmdCount === 0; michael@0: }); michael@0: } michael@0: michael@0: /** michael@0: * Basic test routine helper for mobile message tests. michael@0: * michael@0: * This helper does nothing but clean-ups. michael@0: * michael@0: * @param aTestCaseMain michael@0: * A function that takes no parameter. michael@0: */ michael@0: function startTestBase(aTestCaseMain) { michael@0: Promise.resolve() michael@0: .then(aTestCaseMain) michael@0: .then(cleanUp, function() { michael@0: ok(false, 'promise rejects during test.'); michael@0: cleanUp(); michael@0: }); michael@0: } michael@0: michael@0: /** michael@0: * Common test routine helper for mobile message tests. michael@0: * michael@0: * This function ensures global |manager| variable is available during the michael@0: * process and performs clean-ups as well. michael@0: * michael@0: * @param aTestCaseMain michael@0: * A function that takes no parameter. michael@0: */ michael@0: function startTestCommon(aTestCaseMain) { michael@0: startTestBase(function() { michael@0: return ensureMobileMessage() michael@0: .then(deleteAllMessages) michael@0: .then(aTestCaseMain) michael@0: .then(deleteAllMessages); michael@0: }); michael@0: } michael@0: michael@0: /** michael@0: * Helper to run the test case only needed in Multi-SIM environment. michael@0: * michael@0: * @param aTest michael@0: * A function which will be invoked w/o parameter. michael@0: * @return a Promise object. michael@0: */ michael@0: function runIfMultiSIM(aTest) { michael@0: let numRIL; michael@0: try { michael@0: numRIL = SpecialPowers.getIntPref("ril.numRadioInterfaces"); michael@0: } catch (ex) { michael@0: numRIL = 1; // Pref not set. michael@0: } michael@0: michael@0: if (numRIL > 1) { michael@0: return aTest(); michael@0: } else { michael@0: log("Not a Multi-SIM environment. Test is skipped."); michael@0: return Promise.resolve(); michael@0: } michael@0: }