author | Philipp von Weitershausen <philipp@weitershausen.de> |
Mon, 14 Jan 2013 17:37:54 -0800 | |
changeset 118847 | d97bca5db7470eee55d044541e4c84388f765be6 |
parent 118813 | b04a7b0c22b616a08932fe6aee1e9b100438e643 |
child 118848 | 829470a09f03395c6e5dbeed9cb4b225af7f6a95 |
push id | 24180 |
push user | emorley@mozilla.com |
push date | Tue, 15 Jan 2013 22:58:27 +0000 |
treeherder | mozilla-central@72e34ce7fd92 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | bustage |
bugs | 813978 |
milestone | 21.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
|
dom/sms/src/ril/SmsDatabaseService.js | file | annotate | diff | comparison | revisions | |
dom/sms/tests/marionette/manifest.ini | file | annotate | diff | comparison | revisions | |
dom/sms/tests/marionette/test_filter_mixed.js | file | annotate | diff | comparison | revisions |
--- a/dom/sms/src/ril/SmsDatabaseService.js +++ b/dom/sms/src/ril/SmsDatabaseService.js @@ -10,17 +10,17 @@ Cu.import("resource://gre/modules/XPCOMU Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/PhoneNumberUtils.jsm"); const RIL_SMSDATABASESERVICE_CONTRACTID = "@mozilla.org/sms/rilsmsdatabaseservice;1"; const RIL_SMSDATABASESERVICE_CID = Components.ID("{a1fa610c-eb6c-4ac2-878f-b005d5e89249}"); const DEBUG = false; const DB_NAME = "sms"; -const DB_VERSION = 7; +const DB_VERSION = 6; const STORE_NAME = "sms"; const MOST_RECENT_STORE_NAME = "most-recent"; const DELIVERY_SENDING = "sending"; const DELIVERY_RECEIVED = "received"; const DELIVERY_STATUS_NOT_APPLICABLE = "not-applicable"; const DELIVERY_STATUS_SUCCESS = "success"; @@ -190,20 +190,16 @@ SmsDatabaseService.prototype = { case 4: if (DEBUG) debug("Upgrade to version 5. Populate quick threads view.") self.upgradeSchema4(event.target.transaction); break; case 5: if (DEBUG) debug("Upgrade to version 6. Use PhonenumberJS.") self.upgradeSchema5(event.target.transaction); break; - case 6: - if (DEBUG) debug("Upgrade to version 7. Use multiple entry indexes.") - self.upgradeSchema6(event.target.transaction); - break; default: event.target.transaction.abort(); callback("Old database version: " + event.oldVersion, null); break; } currentVersion++; } } @@ -270,16 +266,19 @@ SmsDatabaseService.prototype = { * Create the initial database schema. * * TODO need to worry about number normalization somewhere... * TODO full text search on body??? */ createSchema: function createSchema(db) { // This objectStore holds the main SMS data. let objectStore = db.createObjectStore(STORE_NAME, { keyPath: "id" }); + objectStore.createIndex("delivery", "delivery", { unique: false }); + objectStore.createIndex("sender", "sender", { unique: false }); + objectStore.createIndex("receiver", "receiver", { unique: false }); objectStore.createIndex("timestamp", "timestamp", { unique: false }); if (DEBUG) debug("Created object stores and indexes"); }, /** * Upgrade to the corresponding database schema version. */ upgradeSchema: function upgradeSchema(objectStore) { @@ -363,261 +362,99 @@ SmsDatabaseService.prototype = { cursor.continue(); } }, upgradeSchema5: function upgradeSchema5(transaction) { // Don't perform any upgrade. See Bug 819560. }, - upgradeSchema6: function upgradeSchema6(transaction) { - let objectStore = transaction.objectStore(STORE_NAME); - - // Delete "delivery" index. - if (objectStore.indexNames.contains("delivery")) { - objectStore.deleteIndex("delivery"); - } - // Delete "sender" index. - if (objectStore.indexNames.contains("sender")) { - objectStore.deleteIndex("sender"); - } - // Delete "receiver" index. - if (objectStore.indexNames.contains("receiver")) { - objectStore.deleteIndex("receiver"); - } - // Delete "read" index. - if (objectStore.indexNames.contains("read")) { - objectStore.deleteIndex("read"); - } - - // Create new "delivery", "number" and "read" indexes. - objectStore.createIndex("delivery", "deliveryIndex"); - objectStore.createIndex("number", "numberIndex", { multiEntry: true }); - objectStore.createIndex("read", "readIndex"); - - // Populate new "deliverIndex", "numberIndex" and "readIndex" attributes. - objectStore.openCursor().onsuccess = function(event) { - let cursor = event.target.result; - if (!cursor) { - return; - } - - let message = cursor.value; - let timestamp = message.timestamp; - message.deliveryIndex = [message.delivery, timestamp]; - message.numberIndex = [ - [message.sender, timestamp], - [message.receiver, timestamp] - ]; - message.readIndex = [message.read, timestamp]; - cursor.update(message); - cursor.continue(); + /** + * Helper function to make the intersection of the partial result arrays + * obtained within createMessageList. + * + * @param keys + * Object containing the partial result arrays. + * @param fiter + * Object containing the filter search criteria used to retrieved the + * partial results. + * + * return Array of keys containing the final result of createMessageList. + */ + keyIntersection: function keyIntersection(keys, filter) { + // Always use keys[FILTER_TIMESTAMP] as base result set to be filtered. + // This ensures the result set is always sorted by timestamp. + let result = keys[FILTER_TIMESTAMP]; + if (keys[FILTER_NUMBERS].length || filter.numbers) { + result = result.filter(function(i) { + return keys[FILTER_NUMBERS].indexOf(i) != -1; + }); } - }, - - createMessageFromRecord: function createMessageFromRecord(record) { - if (DEBUG) debug("createMessageFromRecord: " + JSON.stringify(record)); - return gSmsService.createSmsMessage(record.id, - record.delivery, - record.deliveryStatus, - record.sender, - record.receiver, - record.body, - record.messageClass, - record.timestamp, - record.read); - }, - - /** - * Queue up passed message id, reply if necessary. 'aMessageId' = 0 for no - * more messages, negtive for errors and valid otherwise. - */ - onNextMessageInListGot: function onNextMessageInListGot( - aRequest, aObjectStore, aMessageList, aMessageId) { - - if (DEBUG) debug("onNextMessageInListGot - " + aMessageId); - if (aMessageId) { - // Queue up any id but '0' and replies later accordingly. - aMessageList.results.push(aMessageId); - } - if (aMessageId <= 0) { - // No more processing. - aMessageList.processing = false; - } - - if (!aMessageList.waitCount) { - if (DEBUG) debug("Cursor.continue() not called yet"); - return; + if (keys[FILTER_DELIVERY].length || filter.delivery) { + result = result.filter(function(i) { + return keys[FILTER_DELIVERY].indexOf(i) != -1; + }); } - - aMessageList.waitCount -= 1; - if (!aMessageList.results.length) { - if (!aMessageList.processing) { - if (DEBUG) debug("No messages matching the filter criteria"); - aRequest.notifyNoMessageInList(); - } - return; - } - - if (aMessageList.results[0] < 0) { - aRequest.notifyReadMessageListFailed(Ci.nsISmsRequest.INTERNAL_ERROR); - return; + if (keys[FILTER_READ].length || filter.read) { + result = result.filter(function(i) { + return keys[FILTER_READ].indexOf(i) != -1; + }); } - - let firstMessageId = aMessageList.results.shift(); - if (DEBUG) debug ("Fetching message " + firstMessageId); - let request = aObjectStore.get(aMessageId); - let self = this; - request.onsuccess = function onsuccess(event) { - let sms = self.createMessageFromRecord(event.target.result); - if (aMessageList.listId >= 0) { - if (DEBUG) debug("notifyNextMessageInListGot " + firstMessageId); - aRequest.notifyNextMessageInListGot(sms); - } else { - self.lastMessageListId += 1; - aMessageList.listId = self.lastMessageListId; - self.messageLists[self.lastMessageListId] = aMessageList; - if (DEBUG) debug("notifyMessageListCreated " + firstMessageId); - aRequest.notifyMessageListCreated(aMessageList.listId, sms); - } - } - request.onerror = function onerror(event) { - if (DEBUG) debug("notifyReadMessageListFailed " + firstMessageId); - aRequest.notifyReadMessageListFailed(Ci.nsISmsRequest.INTERNAL_ERROR); - } + return result; }, /** - * Queue up {aMessageId, aTimestamp} pairs, find out intersections and report - * to onNextMessageInListGot. Return true if it is still possible to have - * another match. + * Helper function called after createMessageList gets the final result array + * containing the list of primary keys of records that matches the provided + * search criteria. This function retrieves from the store the message with + * the primary key matching the first one in the message list array and keeps + * the rest of this array in memory. It also notifies via nsISmsRequest. + * + * @param messageList + * Array of primary keys retrieved within createMessageList. + * @param request + * A nsISmsRequest object. */ - onNextMessageInMultiFiltersGot: function onNextMessageInMultiFiltersGot( - aRequest, aObjectStore, aMessageList, aWhich, aMessageId, aTimestamp) { - - if (DEBUG) { - debug("onNextMessageInMultiFiltersGot: " - + aWhich + ", " + aMessageId + ", " + aTimestamp); - } - let filters = aMessageList.filters; - - if (!aMessageId) { - filters[aWhich].processing = false; - for (let i = 0; i < filters.length; i++) { - if (filters[i].processing) { - return false; - } - } - - this.onNextMessageInListGot(aRequest, aObjectStore, aMessageList, 0); - return false; - } - - // Search id in other existing results. If no other results has it, - // and A) the last timestamp is smaller-equal to current timestamp, - // we wait for further results; either B) record timestamp is larger - // then current timestamp or C) no more processing for a filter, then we - // drop this id because there can't be a match anymore. - for (let i = 0; i < filters.length; i++) { - if (i == aWhich) continue; - - let ctx = filters[i]; - let results = ctx.results; - let found = false; - for (let j = 0; j < results.length; j++) { - let result = results[j]; - if (result.id == aMessageId) { - found = true; - break; - } - if ((!aMessageList.reverse && (result.timestamp > aTimestamp)) || - (aMessageList.reverse && (result.timestamp < aTimestamp))) { - // B) Cannot find a match anymore. Drop. - return true; - } + onMessageListCreated: function onMessageListCreated(messageList, aRequest) { + if (DEBUG) debug("Message list created: " + messageList); + let self = this; + self.newTxn(READ_ONLY, function (error, txn, store) { + if (error) { + aRequest.notifyReadMessageListFailed(Ci.nsISmsRequest.INTERNAL_ERROR); + return; } - if (!found) { - if (!ctx.processing) { - // C) Cannot find a match anymore. Drop. - if (results.length) { - let lastResult = results[results.length - 1]; - if ((!aMessageList.reverse && (lastResult.timestamp >= aTimestamp)) || - (aMessageList.reverse && (lastResult.timestamp <= aTimestamp))) { - // Still have a chance to get another match. Return true. - return true; - } - } - - // Impossible to find another match because all results in ctx have - // timestamps smaller than aTimestamp. - return this.onNextMessageInMultiFiltersGot(aRequest, aObjectStore, - aMessageList, aWhich, 0, 0); - } - - // A) Pending. - filters[aWhich].results.push({ - id: aMessageId, - timestamp: aTimestamp - }); - return true; - } - } - - // Now id is found in all other results. Report it. - this.onNextMessageInListGot(aRequest, aObjectStore, aMessageList, aMessageId); - return true; - }, - - onNextMessageInMultiNumbersGot: function onNextMessageInMultiNumbersGot( - aRequest, aObjectStore, aMessageList, aWhich, - aSingleFilter, aIndex, aMessageId, aTimestamp) { + let messageId = messageList.shift(); + if (DEBUG) debug ("Fetching message " + messageId); + let request = store.get(messageId); + let message; + request.onsuccess = function (event) { + message = request.result; + }; - if (DEBUG) { - debug("onNextMessageInMultiNumbersGot: " - + aIndex + ", " + aMessageId + ", " + aTimestamp); - } - let queues = aMessageList.filters[aWhich].queues; - let q = queues[aIndex]; - if (aMessageId) { - if (!aIndex) { // Timestamp. - q.results.push({ - id: aMessageId, - timestamp: aTimestamp - }); - } else { // Numbers. - q.results.push(aMessageId); - } - return true; - } - - q.processing -= 1; - if (queues[0].processing || queues[1].processing) { - return false; - } - - let tres = queues[0].results; - let qres = queues[1].results; - tres = tres.filter(function (element) { - return qres.indexOf(element.id) != -1; + txn.oncomplete = function oncomplete(event) { + if (DEBUG) debug("Transaction " + txn + " completed."); + if (!message) { + aRequest.notifyReadMessageListFailed(Ci.nsISmsRequest.INTERNAL_ERROR); + return; + } + self.lastMessageListId += 1; + self.messageLists[self.lastMessageListId] = messageList; + let sms = gSmsService.createSmsMessage(message.id, + message.delivery, + message.deliveryStatus, + message.sender, + message.receiver, + message.body, + message.messageClass, + message.timestamp, + message.read); + aRequest.notifyMessageListCreated(self.lastMessageListId, sms); + }; }); - if (aSingleFilter) { - for (let i = 0; i < tres.length; i++) { - this.onNextMessageInListGot(aRequest, aObjectStore, aMessageList, tres[i].id); - } - this.onNextMessageInListGot(aRequest, aObjectStore, aMessageList, 0); - } else { - for (let i = 0; i < tres.length; i++) { - this.onNextMessageInMultiFiltersGot(aRequest, aObjectStore, aMessageList, - aWhich, tres[i].id, tres[i].timestamp); - } - this.onNextMessageInMultiFiltersGot(aRequest, aObjectStore, aMessageList, - aWhich, 0, 0); - } - return false; }, saveMessage: function saveMessage(message) { this.lastKey += 1; message.id = this.lastKey; if (DEBUG) debug("Going to store " + JSON.stringify(message)); this.newTxn(READ_WRITE, function(error, txn, stores) { if (error) { @@ -685,30 +522,24 @@ SmsDatabaseService.prototype = { let sender = aSender; if (sender) { let parsedNumber = PhoneNumberUtils.parse(sender); sender = (parsedNumber && parsedNumber.internationalNumber) ? parsedNumber.internationalNumber : sender; } - let message = { - deliveryIndex: [DELIVERY_RECEIVED, aDate], - numberIndex: [[sender, aDate], [receiver, aDate]], - readIndex: [FILTER_READ_UNREAD, aDate], - - delivery: DELIVERY_RECEIVED, - deliveryStatus: DELIVERY_STATUS_SUCCESS, - sender: sender, - receiver: receiver, - body: aBody, - messageClass: aMessageClass, - timestamp: aDate, - read: FILTER_READ_UNREAD - }; + let message = {delivery: DELIVERY_RECEIVED, + deliveryStatus: DELIVERY_STATUS_SUCCESS, + sender: sender, + receiver: receiver, + body: aBody, + messageClass: aMessageClass, + timestamp: aDate, + read: FILTER_READ_UNREAD}; return this.saveMessage(message); }, saveSendingMessage: function saveSendingMessage(aReceiver, aBody, aDate) { let sender = this.mRIL.rilContext.icc ? this.mRIL.rilContext.icc.msisdn : null; // Workaround an xpconnect issue with undefined string objects. // See bug 808220 @@ -726,30 +557,24 @@ SmsDatabaseService.prototype = { if (sender) { let parsedNumber = PhoneNumberUtils.parse(sender.toString()); sender = (parsedNumber && parsedNumber.internationalNumber) ? parsedNumber.internationalNumber : sender; } - let message = { - deliveryIndex: [DELIVERY_SENDING, aDate], - numberIndex: [[sender, aDate], [receiver, aDate]], - readIndex: [FILTER_READ_READ, aDate], - - delivery: DELIVERY_SENDING, - deliveryStatus: DELIVERY_STATUS_PENDING, - sender: sender, - receiver: receiver, - body: aBody, - messageClass: MESSAGE_CLASS_NORMAL, - timestamp: aDate, - read: FILTER_READ_READ - }; + let message = {delivery: DELIVERY_SENDING, + deliveryStatus: DELIVERY_STATUS_PENDING, + sender: sender, + receiver: receiver, + body: aBody, + messageClass: MESSAGE_CLASS_NORMAL, + timestamp: aDate, + read: FILTER_READ_READ}; return this.saveMessage(message); }, setMessageDelivery: function setMessageDelivery(messageId, delivery, deliveryStatus) { if (DEBUG) { debug("Setting message " + messageId + " delivery to " + delivery + ", and deliveryStatus to " + deliveryStatus); } @@ -778,34 +603,32 @@ SmsDatabaseService.prototype = { && (message.deliveryStatus == deliveryStatus)) { if (DEBUG) { debug("The values of attribute delivery and deliveryStatus are the" + " the same with given parameters."); } return; } message.delivery = delivery; - message.deliveryIndex = [delivery, message.timestamp]; message.deliveryStatus = deliveryStatus; if (DEBUG) { debug("Message.delivery set to: " + delivery + ", and Message.deliveryStatus set to: " + deliveryStatus); } store.put(message); }; }); }, /** * nsISmsDatabaseService API */ getMessage: function getMessage(messageId, aRequest) { if (DEBUG) debug("Retrieving message with ID " + messageId); - let self = this; this.newTxn(READ_ONLY, function (error, txn, store) { if (error) { if (DEBUG) debug(error); aRequest.notifyGetMessageFailed(Ci.nsISmsRequest.INTERNAL_ERROR); return; } let request = store.mozGetAll(messageId); @@ -825,18 +648,26 @@ SmsDatabaseService.prototype = { if (data.id != messageId) { if (DEBUG) { debug("Requested message ID (" + messageId + ") is " + "different from the one we got"); } aRequest.notifyGetMessageFailed(Ci.nsISmsRequest.UNKNOWN_ERROR); return; } - let sms = self.createMessageFromRecord(data); - aRequest.notifyMessageGot(sms); + let message = gSmsService.createSmsMessage(data.id, + data.delivery, + data.deliveryStatus, + data.sender, + data.receiver, + data.body, + data.messageClass, + data.timestamp, + data.read); + aRequest.notifyMessageGot(message); }; txn.onerror = function onerror(event) { if (DEBUG) { if (event.target) debug("Caught error on transaction", event.target.errorCode); } //TODO look at event.target.errorCode, pick appropriate error constant @@ -885,39 +716,54 @@ SmsDatabaseService.prototype = { // This must exist. let mostRecentEntry = event.target.result; if (!message.read) { mostRecentEntry.unreadCount--; } if (mostRecentEntry.id == messageId) { - // Check most recent sender/receiver. - let numberRange = IDBKeyRange.bound([number, 0], [number, ""]); - let numberRequest = smsStore.index("number") - .openCursor(numberRange, PREV); - numberRequest.onsuccess = function(event) { + // This sucks, we have to find a new most-recent message. + message = null; + + // Check most recent sender. + smsStore.index("sender").openCursor(number, "prev").onsuccess = function(event) { + let cursor = event.target.result; + if (cursor) { + message = cursor.value; + } + }; + + // Check most recent receiver. + smsStore.index("receiver").openCursor(number, "prev").onsuccess = function(event) { let cursor = event.target.result; - if (!cursor) { + if (cursor) { + if (!message || cursor.value.timeStamp > message.timestamp) { + message = cursor.value; + } + } + + // If we found a new message then we need to update the data + // in the most-recent store. Otherwise we can delete it. + if (message) { + mostRecentEntry.id = message.id; + mostRecentEntry.timestamp = message.timestamp; + mostRecentEntry.body = message.body; + if (DEBUG) { + debug("Updating mru entry: " + + JSON.stringify(mostRecentEntry)); + } + mruStore.put(mostRecentEntry); + } + else { if (DEBUG) { debug("Deleting mru entry for number '" + number + "'"); } mruStore.delete(number); - return; } - - let nextMsg = cursor.value; - mostRecentEntry.id = nextMsg.id; - mostRecentEntry.timestamp = nextMsg.timestamp; - mostRecentEntry.body = nextMsg.body; - if (DEBUG) { - debug("Updating mru entry: " + - JSON.stringify(mostRecentEntry)); - } - mruStore.put(mostRecentEntry); }; } else if (!message.read) { // Shortcut, just update the unread count. if (DEBUG) { debug("Updating unread count for number '" + number + "': " + (mostRecentEntry.unreadCount + 1) + " -> " + mostRecentEntry.unreadCount); } @@ -937,336 +783,200 @@ SmsDatabaseService.prototype = { debug("Creating a message list. Filters:" + " startDate: " + filter.startDate + " endDate: " + filter.endDate + " delivery: " + filter.delivery + " numbers: " + filter.numbers + " read: " + filter.read + " reverse: " + reverse); } + // This object keeps the lists of keys retrieved by the search specific to + // each nsIMozSmsFilter. Once all the keys have been retrieved from the + // store, the final intersection of this arrays will contain all the + // keys for the message list that we are creating. + let filteredKeys = {}; + filteredKeys[FILTER_TIMESTAMP] = []; + filteredKeys[FILTER_NUMBERS] = []; + filteredKeys[FILTER_DELIVERY] = []; + filteredKeys[FILTER_READ] = []; + + // Callback function to iterate through request results via IDBCursor. + let successCb = function onsuccess(result, filter) { + // Once the cursor has retrieved all keys that matches its key range, + // the filter search is done. + if (!result) { + if (DEBUG) { + debug("These messages match the " + filter + " filter: " + + filteredKeys[filter]); + } + return; + } + // The cursor primaryKey is stored in its corresponding partial array + // according to the filter parameter. + let primaryKey = result.primaryKey; + filteredKeys[filter].push(primaryKey); + result.continue(); + }; + + let errorCb = function onerror(event) { + //TODO look at event.target.errorCode, pick appropriate error constant. + if (DEBUG) debug("IDBRequest error " + event.target.errorCode); + aRequest.notifyReadMessageListFailed(Ci.nsISmsRequest.INTERNAL_ERROR); + return; + }; let self = this; this.newTxn(READ_ONLY, function (error, txn, store) { if (error) { - //TODO look at event.target.errorCode, pick appropriate error constant. - if (DEBUG) debug("IDBRequest error " + error.target.errorCode); - aRequest.notifyReadMessageListFailed(Ci.nsISmsRequest.INTERNAL_ERROR); + errorCb(error); return; } - let messageList = { - listId: -1, - reverse: reverse, - processing: true, - stop: false, - // Local contexts for multiple filter targets' case. - filters: [], - // Pending message waiting count. Initialized with 1 for notifying - // message list created. - waitCount: 1, - results: [] + // In first place, we retrieve the keys that match the filter.startDate + // and filter.endDate search criteria. + let timeKeyRange = null; + if (filter.startDate != null && filter.endDate != null) { + timeKeyRange = IDBKeyRange.bound(filter.startDate.getTime(), + filter.endDate.getTime()); + } else if (filter.startDate != null) { + timeKeyRange = IDBKeyRange.lowerBound(filter.startDate.getTime()); + } else if (filter.endDate != null) { + timeKeyRange = IDBKeyRange.upperBound(filter.endDate.getTime()); + } + let direction = reverse ? PREV : NEXT; + let timeRequest = store.index("timestamp").openKeyCursor(timeKeyRange, + direction); + + timeRequest.onsuccess = function onsuccess(event) { + successCb(event.target.result, FILTER_TIMESTAMP); }; + timeRequest.onerror = errorCb; - let onNextMessageInListGotCb = - self.onNextMessageInListGot.bind(self, aRequest, store, messageList); + // Retrieve the keys from the 'delivery' index that matches the + // value of filter.delivery. + if (filter.delivery) { + let deliveryKeyRange = IDBKeyRange.only(filter.delivery); + let deliveryRequest = store.index("delivery") + .openKeyCursor(deliveryKeyRange); + deliveryRequest.onsuccess = function onsuccess(event) { + successCb(event.target.result, FILTER_DELIVERY); + }; + deliveryRequest.onerror = errorCb; + } - let singleFilterSuccessCb = function onSingleFilterSuccess(event) { - if (messageList.stop) { - return; + // Retrieve the keys from the 'sender' and 'receiver' indexes that + // match the values of filter.numbers + if (filter.numbers) { + for (let i = 0; i < filter.numbers.length; i++) { + let numberKeyRange = IDBKeyRange.only(filter.numbers[i]); + let senderRequest = store.index("sender") + .openKeyCursor(numberKeyRange); + let receiverRequest = store.index("receiver") + .openKeyCursor(numberKeyRange); + senderRequest.onsuccess = receiverRequest.onsuccess = + function onsuccess(event){ + successCb(event.target.result, FILTER_NUMBERS); + }; + senderRequest.onerror = receiverRequest.onerror = errorCb; } + } - let cursor = event.target.result; - // Once the cursor has retrieved all keys that matches its key range, - // the filter search is done. - if (cursor) { - onNextMessageInListGotCb(cursor.primaryKey); - cursor.continue(); - } else { - onNextMessageInListGotCb(0); - } - }; + // Retrieve the keys from the 'read' index that matches the value of + // filter.read + if (filter.read != undefined) { + let read = filter.read ? FILTER_READ_READ : FILTER_READ_UNREAD; + if (DEBUG) debug("filter.read " + read); + let readKeyRange = IDBKeyRange.only(read); + let readRequest = store.index("read") + .openKeyCursor(readKeyRange); + readRequest.onsuccess = function onsuccess(event) { + successCb(event.target.result, FILTER_READ); + }; + readRequest.onerror = errorCb; + } - let singleFilterErrorCb = function onSingleFilterError(event) { - if (messageList.stop) { + txn.oncomplete = function oncomplete(event) { + if (DEBUG) debug("Transaction " + txn + " completed."); + // We need to get the intersection of all the partial searches to + // get the final result array. + let result = self.keyIntersection(filteredKeys, filter); + if (!result.length) { + if (DEBUG) debug("No messages matching the filter criteria"); + aRequest.notifyNoMessageInList(); return; } - if (DEBUG) debug("IDBRequest error " + event.target.errorCode); - onNextMessageInListGotCb(-1); + // At this point, filteredKeys should have all the keys that matches + // all the search filters. So we take the first key and retrieve the + // corresponding message. The rest of the keys are added to the + // messageLists object as a new list. + self.onMessageListCreated(result, aRequest); }; - let direction = reverse ? PREV : NEXT; - - // We support filtering by date range only (see `else` block below) or - // by number/delivery status/read status with an optional date range. - if (filter.delivery || filter.numbers || filter.read != undefined) { - let multiFiltersGotCb = self.onNextMessageInMultiFiltersGot - .bind(self, aRequest, store, messageList); - - let multiFiltersSuccessCb = function onMultiFiltersSuccess(which, event) { - if (messageList.stop) { - return; - } - - let cursor = event.target.result; - if (cursor) { - if (multiFiltersGotCb(which, cursor.primaryKey, cursor.key[1])) { - cursor.continue(); - } - } else { - multiFiltersGotCb(which, 0, 0); - } - }; - - let multiFiltersErrorCb = function onMultiFiltersError(which, event) { - if (messageList.stop) { - return; - } - - // Act as no more matched records. - multiFiltersGotCb(which, 0, 0); - }; - - // Numeric 0 is smaller than any time stamp, and empty string is larger - // than all numeric values. - let startDate = 0, endDate = ""; - if (filter.startDate != null) { - startDate = filter.startDate.getTime(); - } - if (filter.endDate != null) { - endDate = filter.endDate.getTime(); - } - - let singleFilter; - { - let numFilterTargets = 0; - if (filter.delivery) numFilterTargets++; - if (filter.numbers) numFilterTargets++; - if (filter.read != undefined) numFilterTargets++; - singleFilter = numFilterTargets == 1; - } - - let which = 0; - - let createRangedRequest = function createRangedRequest(indexName, key) { - let range = IDBKeyRange.bound([key, startDate], [key, endDate]); - return store.index(indexName).openKeyCursor(range, direction); - }; - - let createSimpleRangedRequest = - function createSimpleRangedRequest(indexName, key) { - let request = createRangedRequest(indexName, key); - if (singleFilter) { - request.onsuccess = singleFilterSuccessCb; - request.onerror = singleFilterErrorCb; - } else { - let me = which++; - messageList.filters.push({ - processing: true, - results: [] - }); - request.onsuccess = multiFiltersSuccessCb.bind(null, me); - request.onerror = multiFiltersErrorCb.bind(null, me); - } - }; - - // Retrieve the keys from the 'delivery' index that matches the - // value of filter.delivery. - if (filter.delivery) { - if (DEBUG) debug("filter.delivery " + filter.delivery); - createSimpleRangedRequest("delivery", filter.delivery); - } - - // Retrieve the keys from the 'sender' and 'receiver' indexes that - // match the values of filter.numbers - if (filter.numbers) { - if (DEBUG) debug("filter.numbers " + filter.numbers.join(", ")); - let me = which++; - - let multiNumbersGotCb = self.onNextMessageInMultiNumbersGot - .bind(self, aRequest, store, messageList, - me, singleFilter); - - let multiNumbersSuccessCb = function onMultiNumbersSuccess(index, event) { - if (messageList.stop) { - return; - } - - let cursor = event.target.result; - if (cursor) { - if (multiNumbersGotCb(index, cursor.primaryKey, - index ? cursor.key[1] : cursor.key)) { - cursor.continue(); - } - } else { - multiNumbersGotCb(index, 0, 0); - } - }; - - let multiNumbersErrorCb = function onMultiNumbersError(index, event) { - if (messageList.stop) { - return; - } - - // Act as no more matched records. - multiNumbersGotCb(index, 0, 0); - }; - - let ctx = {}; - if (!singleFilter) { - ctx.processing = true; - ctx.results = []; - } - - let multiNumbers = filter.numbers.length > 1; - if (multiNumbers) { - ctx.queues = []; - // For timestamp. - let range = null; - if (filter.startDate != null && filter.endDate != null) { - range = IDBKeyRange.bound(filter.startDate.getTime(), - filter.endDate.getTime()); - } else if (filter.startDate != null) { - range = IDBKeyRange.lowerBound(filter.startDate.getTime()); - } else if (filter.endDate != null) { - range = IDBKeyRange.upperBound(filter.endDate.getTime()); - } - - let request = store.index("timestamp") - .openKeyCursor(range, direction); - request.onsuccess = multiNumbersSuccessCb.bind(null, 0); - request.onerror = multiNumbersErrorCb.bind(null, 0); - - ctx.queues.push({ - processing: 1, - results: [] - }); - // For all numbers. - ctx.queues.push({ - processing: filter.numbers.length, - results: [] - }); - } - - messageList.filters.push(ctx); - - for (let i = 0; i < filter.numbers.length; i++) { - let request = createRangedRequest("number", filter.numbers[i]); - if (multiNumbers) { - request.onsuccess = multiNumbersSuccessCb.bind(null, 1); - request.onerror = multiNumbersErrorCb.bind(null, 1); - } else if (singleFilter) { - request.onsuccess = singleFilterSuccessCb; - request.onerror = singleFilterErrorCb; - } else { - request.onsuccess = multiFiltersSuccessCb.bind(null, me); - request.onerror = multiFiltersErrorCb.bind(null, me); - } - } - } - - // Retrieve the keys from the 'read' index that matches the value of - // filter.read - if (filter.read != undefined) { - let read = filter.read ? FILTER_READ_READ : FILTER_READ_UNREAD; - if (DEBUG) debug("filter.read " + read); - createSimpleRangedRequest("read", read); - } - } else { - // Filtering by date range only. - if (DEBUG) { - debug("filter.timestamp " + filter.startDate + ", " + filter.endDate); - } - - let range = null; - if (filter.startDate != null && filter.endDate != null) { - range = IDBKeyRange.bound(filter.startDate.getTime(), - filter.endDate.getTime()); - } else if (filter.startDate != null) { - range = IDBKeyRange.lowerBound(filter.startDate.getTime()); - } else if (filter.endDate != null) { - range = IDBKeyRange.upperBound(filter.endDate.getTime()); - } - - let request = store.index("timestamp").openKeyCursor(range, direction); - request.onsuccess = singleFilterSuccessCb; - request.onerror = singleFilterErrorCb; - } - - if (DEBUG) { - txn.oncomplete = function oncomplete(event) { - debug("Transaction " + txn + " completed."); - }; - } - - txn.onerror = singleFilterErrorCb; + txn.onerror = function onerror(event) { + errorCb(event); + }; }); }, getNextMessageInList: function getNextMessageInList(listId, aRequest) { if (DEBUG) debug("Getting next message in list " + listId); let messageId; let list = this.messageLists[listId]; if (!list) { if (DEBUG) debug("Wrong list id"); aRequest.notifyReadMessageListFailed(Ci.nsISmsRequest.NOT_FOUND_ERROR); return; } - if (list.processing) { - // Database transaction ongoing, let it reply for us so that we won't get - // blocked by the existing transaction. - list.waitCount++; - return; - } - if (!list.results.length) { + messageId = list.shift(); + if (messageId == null) { if (DEBUG) debug("Reached the end of the list!"); aRequest.notifyNoMessageInList(); return; } - if (list.results[0] < 0) { - aRequest.notifyReadMessageListFailed(Ci.nsISmsRequest.INTERNAL_ERROR); - return; - } - messageId = list.results.shift(); - let self = this; this.newTxn(READ_ONLY, function (error, txn, store) { if (DEBUG) debug("Fetching message " + messageId); let request = store.get(messageId); let message; request.onsuccess = function onsuccess(event) { message = request.result; }; txn.oncomplete = function oncomplete(event) { if (DEBUG) debug("Transaction " + txn + " completed."); if (!message) { if (DEBUG) debug("Could not get message id " + messageId); aRequest.notifyReadMessageListFailed(Ci.nsISmsRequest.NOT_FOUND_ERROR); } - let sms = self.createMessageFromRecord(message); + let sms = gSmsService.createSmsMessage(message.id, + message.delivery, + message.deliveryStatus, + message.sender, + message.receiver, + message.body, + message.messageClass, + message.timestamp, + message.read); aRequest.notifyNextMessageInListGot(sms); }; txn.onerror = function onerror(event) { //TODO check event.target.errorCode if (DEBUG) { debug("Error retrieving message id: " + messageId + ". Error code: " + event.target.errorCode); } aRequest.notifyReadMessageListFailed(Ci.nsISmsRequest.INTERNAL_ERROR); }; }); }, clearMessageList: function clearMessageList(listId) { if (DEBUG) debug("Clearing message list: " + listId); - if (this.messageLists[listId]) { - this.messageLists[listId].stop = true; - delete this.messageLists[listId]; - } + delete this.messageLists[listId]; }, markMessageRead: function markMessageRead(messageId, value, aRequest) { if (DEBUG) debug("Setting message " + messageId + " read to " + value); this.newTxn(READ_WRITE, function (error, txn, stores) { if (error) { if (DEBUG) debug(error); aRequest.notifyMarkMessageReadFailed(Ci.nsISmsRequest.INTERNAL_ERROR); @@ -1294,17 +1004,16 @@ SmsDatabaseService.prototype = { // If the value to be set is the same as the current message `read` // value, we just notify successfully. if (message.read == value) { if (DEBUG) debug("The value of message.read is already " + value); aRequest.notifyMessageMarkedRead(message.read); return; } message.read = value ? FILTER_READ_READ : FILTER_READ_UNREAD; - message.readIndex = [message.read, message.timestamp]; if (DEBUG) debug("Message.read set to: " + value); event.target.source.put(message).onsuccess = function onsuccess(event) { if (DEBUG) { debug("Update successfully completed. Message: " + JSON.stringify(event.target.result)); } // Now update the unread count.
--- a/dom/sms/tests/marionette/manifest.ini +++ b/dom/sms/tests/marionette/manifest.ini @@ -16,15 +16,14 @@ qemu = true [test_filter_date.js] [test_filter_date_notfound.js] [test_filter_number_single.js] [test_filter_number_multiple.js] [test_filter_received.js] [test_filter_sent.js] [test_filter_read.js] [test_filter_unread.js] -[test_filter_mixed.js] [test_segment_info.js] [test_mark_msg_read.js] [test_mark_msg_read_error.js] [test_bug814761.js] [test_strict_7bit_encoding.js] disabled = Bug 828633
deleted file mode 100644 --- a/dom/sms/tests/marionette/test_filter_mixed.js +++ /dev/null @@ -1,338 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -MARIONETTE_TIMEOUT = 40000; - -const SELF = 5554; -const NUM_THREADS = 10; - -SpecialPowers.addPermission("sms", true, document); -SpecialPowers.setBoolPref("dom.sms.enabled", true); - -let sms = window.navigator.mozSms; -ok(sms instanceof MozSmsManager); - -let pendingEmulatorCmdCount = 0; -function sendSmsToEmulator(from, text) { - ++pendingEmulatorCmdCount; - - let cmd = "sms send " + from + " " + text; - runEmulatorCmd(cmd, function (result) { - --pendingEmulatorCmdCount; - - is(result[0], "OK", "Emulator response"); - }); -} - -function getAllMessages(callback, filter, reverse) { - if (!filter) { - filter = new MozSmsFilter; - } - let messages = []; - let request = sms.getMessages(filter, reverse || false); - request.onsuccess = function(event) { - let cursor = event.target.result; - if (cursor.message) { - messages.push(cursor.message); - cursor.continue(); - return; - } - - window.setTimeout(callback.bind(null, messages), 0); - } -} - -function deleteAllMessages(next) { - getAllMessages(function deleteAll(messages) { - let message = messages.shift(); - if (!message) { - ok(true, "all messages deleted"); - window.setTimeout(next, 0); - return; - } - - let request = sms.delete(message.id); - request.onsuccess = deleteAll.bind(null, messages); - request.onerror = function (event) { - ok(false, "failed to delete all messages"); - window.setTimeout(cleanUp, 0); - } - }); -} - -/** - * Populate SmsDatabase with messages to being tests. We'll have NUM_THREADS - * sent and received messages, and NUM_THREADS/2 unread received messages. - * - * send to "0" - * receive from "0", count = 1 - * mark received as read - * - * send to "1" - * receive from "1", count = 2 - * - * send to "2" - * receive from "2", count = 3 - * mark received as read - * ... - * send to "9" - * receive from "9", count = 10 - */ -function populateMessages() { - let count = 0; - - function sendMessage(iter) { - let request = sms.send("" + iter, "Nice to meet you"); - request.onsuccess = function onRequestSuccess(event) { - sms.addEventListener("received", onReceived); - sendSmsToEmulator("" + iter, "Nice to meet you, too"); - } - request.onerror = function onRequestError(event) { - window.setTimeout(deleteAllMessages.bind(null, cleanUp), 0); - } - } - - function onReceived(event) { - sms.removeEventListener("received", onReceived); - - ++count; - if (count % 2) { - let request = sms.markMessageRead(event.message.id, true); - request.onsuccess = function onRequestSuccess(event) { - if (count < NUM_THREADS) { - sendMessage(count); - } else { - window.setTimeout(testDeliveryAndNumber, 0); - } - } - request.onerror = function onRequestError(event) { - window.setTimeout(deleteAllMessages.bind(null, cleanUp), 0); - } - } else if (count < NUM_THREADS) { - sendMessage(count); - } else { - window.setTimeout(testDeliveryAndNumber, 0); - } - } - - sendMessage(count); -} - -function testDeliveryAndNumber() { - log("Checking delivery == sent && number == 0"); - let filter = new MozSmsFilter(); - filter.delivery = "sent"; - filter.numbers = ["0"]; - getAllMessages(function (messages) { - // Only { delivery: "sent", receiver: "0", read: true } - is(messages.length, 1, "message count"); - for (let i = 0; i < messages.length; i++) { - let message = messages[i]; - is(message.delivery, filter.delivery, "message delivery"); - if (!((message.sender == filter.numbers[0]) - || (message.receiver == filter.numbers[0]))) { - ok(false, "message sendor or receiver number"); - } - } - - getAllMessages(function (messages_r) { - is(messages.length, messages_r.length, "message count"); - for (let i = 0; i < messages_r.length; i++) { - is(messages_r[i].id, messages[messages.length - 1 - i].id, "message id"); - } - - window.setTimeout(testDeliveryAndNumberNotFound, 0); - }, filter, true); - }, filter); -} - -function testDeliveryAndNumberNotFound() { - log("Checking delivery == sent && number == 12345"); - let filter = new MozSmsFilter(); - filter.delivery = "sent"; - filter.numbers = ["12345"]; - getAllMessages(function (messages) { - is(messages.length, 0, "message count"); - - window.setTimeout(testDeliveryAndRead, 0); - }, filter); -} - -function testDeliveryAndRead() { - log("Checking delivery == received && read == true"); - let filter = new MozSmsFilter(); - filter.delivery = "received"; - filter.read = true; - getAllMessages(function (messages) { - // { delivery: "received", sender: "0", read: true }, - // { delivery: "received", sender: "2", read: true }, - // { delivery: "received", sender: "4", read: true }, - // { delivery: "received", sender: "6", read: true }, and - // { delivery: "received", sender: "8", read: true }, - is(messages.length, NUM_THREADS / 2, "message count"); - for (let i = 0; i < messages.length; i++) { - let message = messages[i]; - is(message.delivery, filter.delivery, "message delivery"); - is(message.read, filter.read, "message read"); - } - - getAllMessages(function (messages_r) { - is(messages.length, messages_r.length, "message count"); - for (let i = 0; i < messages_r.length; i++) { - is(messages_r[i].id, messages[messages.length - 1 - i].id, "message id"); - } - - window.setTimeout(testDeliveryAndReadNotFound, 0); - }, filter, true); - }, filter); -} - -function testDeliveryAndReadNotFound() { - log("Checking delivery == sent && read == false"); - let filter = new MozSmsFilter(); - filter.delivery = "sent"; - filter.read = false; - getAllMessages(function (messages) { - is(messages.length, 0, "message count"); - - window.setTimeout(testNumberAndRead, 0); - }, filter); -} - -function testNumberAndRead() { - log("Checking number == 0 && read == true"); - let filter = new MozSmsFilter(); - filter.numbers = ["0"]; - filter.read = true; - getAllMessages(function (messages) { - // { delivery: "sent", receiver: "0", read: true }, and - // { delivery: "received", sender: "0", read: true } - is(messages.length, 2, "message count"); - for (let i = 0; i < messages.length; i++) { - let message = messages[i]; - if (!((message.sender == filter.numbers[0]) - || (message.receiver == filter.numbers[0]))) { - ok(false, "message sendor or receiver number"); - } - is(message.read, filter.read, "message read"); - } - - getAllMessages(function (messages_r) { - is(messages.length, messages_r.length, "message count"); - for (let i = 0; i < messages_r.length; i++) { - is(messages_r[i].id, messages[messages.length - 1 - i].id, "message id"); - } - - window.setTimeout(testNumberAndReadNotFound, 0); - }, filter, true); - }, filter); -} - -function testNumberAndReadNotFound() { - log("Checking number == 12345 && read == true"); - let filter = new MozSmsFilter(); - filter.numbers = ["12345"]; - filter.read = true; - getAllMessages(function (messages) { - is(messages.length, 0, "message count"); - - window.setTimeout(testMultipleNumbers, 0); - }, filter); -} - -function testMultipleNumbers() { - log("Checking number == 0 || number == 1"); - let filter = new MozSmsFilter(); - filter.numbers = ["0", "1"]; - getAllMessages(function (messages) { - // { delivery: "sent", receiver: "0", read: true } - // { delivery: "received", sender: "0", read: true } - // { delivery: "sent", receiver: "1", read: true } - // { delivery: "received", sender: "1", read: false } - is(messages.length, 4, "message count"); - for (let i = 0; i < messages.length; i++) { - let message = messages[i]; - if (!((message.sender == filter.numbers[0]) - || (message.receiver == filter.numbers[0]) - || (message.sender == filter.numbers[1]) - || (message.receiver == filter.numbers[1]))) { - ok(false, "message sendor or receiver number"); - } - } - - window.setTimeout(testMultipleNumbersNotFound, 0); - }, filter); -} - -function testMultipleNumbersNotFound() { - log("Checking number == 12345 || number == 6789"); - let filter = new MozSmsFilter(); - filter.numbers = ["12345", "6789"]; - getAllMessages(function (messages) { - is(messages.length, 0, "message count"); - - window.setTimeout(testDeliveryAndMultipleNumbers, 0); - }, filter); -} - -function testDeliveryAndMultipleNumbers() { - log("Checking delivery == sent && (number == 0 || number == 1)"); - let filter = new MozSmsFilter(); - filter.delivery = "sent"; - filter.numbers = ["0", "1"]; - getAllMessages(function (messages) { - // { delivery: "sent", receiver: "0", read: true } - // { delivery: "sent", receiver: "1", read: true } - is(messages.length, 2, "message count"); - for (let i = 0; i < messages.length; i++) { - let message = messages[i]; - is(message.delivery, filter.delivery, "message delivery"); - if (!((message.sender == filter.numbers[0]) - || (message.receiver == filter.numbers[0]) - || (message.sender == filter.numbers[1]) - || (message.receiver == filter.numbers[1]))) { - ok(false, "message sendor or receiver number"); - } - } - - window.setTimeout(testMultipleNumbersAndRead, 0); - }, filter); -} - -function testMultipleNumbersAndRead() { - log("Checking (number == 0 || number == 1) && read == true"); - let filter = new MozSmsFilter(); - filter.numbers = ["0", "1"]; - filter.read = true; - getAllMessages(function (messages) { - // { delivery: "sent", receiver: "0", read: true } - // { delivery: "received", sender: "0", read: true } - // { delivery: "sent", receiver: "1", read: true } - is(messages.length, 3, "message count"); - for (let i = 0; i < messages.length; i++) { - let message = messages[i]; - is(message.read, filter.read, "message read"); - if (!((message.sender == filter.numbers[0]) - || (message.receiver == filter.numbers[0]) - || (message.sender == filter.numbers[1]) - || (message.receiver == filter.numbers[1]))) { - ok(false, "message sendor or receiver number"); - } - } - - window.setTimeout(deleteAllMessages.bind(null, cleanUp), 0); - }, filter); -} - -function cleanUp() { - if (pendingEmulatorCmdCount) { - window.setTimeout(cleanUp, 100); - return; - } - - SpecialPowers.removePermission("sms", document); - SpecialPowers.clearUserPref("dom.sms.enabled"); - finish(); -} - -deleteAllMessages(populateMessages);