зеркало из https://github.com/mozilla/gecko-dev.git
Bug 880563 - B2G MMS: deleting an MMS during the sending or retrieving process doesn't work properly. r=vicamo,ctai a=leo+
This commit is contained in:
Родитель
d19eab7644
Коммит
1e2d59faea
|
@ -33,11 +33,18 @@ const kSmsRetrievingObserverTopic = "sms-retrieving";
|
|||
const kNetworkInterfaceStateChangedTopic = "network-interface-state-changed";
|
||||
const kXpcomShutdownObserverTopic = "xpcom-shutdown";
|
||||
const kPrefenceChangedObserverTopic = "nsPref:changed";
|
||||
const kMobileMessageDeletedObserverTopic = "mobile-message-deleted";
|
||||
|
||||
// HTTP status codes:
|
||||
// @see http://tools.ietf.org/html/rfc2616#page-39
|
||||
const HTTP_STATUS_OK = 200;
|
||||
|
||||
// Non-standard HTTP status for internal use.
|
||||
const _HTTP_STATUS_USER_CANCELLED = -1;
|
||||
|
||||
// Non-standard MMS status for internal use.
|
||||
const _MMS_ERROR_MESSAGE_DELETED = -1;
|
||||
|
||||
const CONFIG_SEND_REPORT_NEVER = 0;
|
||||
const CONFIG_SEND_REPORT_DEFAULT_NO = 1;
|
||||
const CONFIG_SEND_REPORT_DEFAULT_YES = 2;
|
||||
|
@ -397,7 +404,7 @@ MmsProxyFilter.prototype = {
|
|||
};
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "gMmsTransactionHelper", function () {
|
||||
return {
|
||||
let helper = {
|
||||
/**
|
||||
* Send MMS request to MMSC.
|
||||
*
|
||||
|
@ -413,14 +420,55 @@ XPCOMUtils.defineLazyGetter(this, "gMmsTransactionHelper", function () {
|
|||
*/
|
||||
sendRequest: function sendRequest(method, url, istream, callback) {
|
||||
// TODO: bug 810226 - Support GPRS bearer for MMS transmission and reception.
|
||||
let cancellable = {
|
||||
callback: callback,
|
||||
|
||||
gMmsConnection.acquire((function (method, url, istream, callback,
|
||||
connected) {
|
||||
if (!connected) {
|
||||
// Connection timeout or failed. Report error.
|
||||
isDone: false,
|
||||
isCancelled: false,
|
||||
|
||||
cancel: function cancel() {
|
||||
if (this.isDone) {
|
||||
// It's too late to cancel.
|
||||
return;
|
||||
}
|
||||
|
||||
this.isCancelled = true;
|
||||
if (this.isAcquiringConn) {
|
||||
// We cannot cancel data connection setup here, so we invoke done()
|
||||
// here and handle |cancellable.isDone| in callback function of
|
||||
// |gMmsConnection.acquire|.
|
||||
this.done(_HTTP_STATUS_USER_CANCELLED, null);
|
||||
} else if (this.xhr) {
|
||||
// Client has already sent the HTTP request. Try to abort it.
|
||||
this.xhr.abort();
|
||||
}
|
||||
},
|
||||
|
||||
done: function done(httpStatus, data) {
|
||||
this.isDone = true;
|
||||
if (!this.callback) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.isCancelled) {
|
||||
this.callback(_HTTP_STATUS_USER_CANCELLED, null);
|
||||
} else {
|
||||
this.callback(httpStatus, data);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
cancellable.isAcquiringConn =
|
||||
!gMmsConnection.acquire((function (connected) {
|
||||
|
||||
cancellable.isAcquiringConn = false;
|
||||
|
||||
if (!connected || cancellable.isCancelled) {
|
||||
gMmsConnection.release();
|
||||
if (callback) {
|
||||
callback(0, null);
|
||||
|
||||
if (!cancellable.isDone) {
|
||||
cancellable.done(cancellable.isCancelled ?
|
||||
_HTTP_STATUS_USER_CANCELLED : 0, null);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -429,84 +477,90 @@ XPCOMUtils.defineLazyGetter(this, "gMmsTransactionHelper", function () {
|
|||
let proxyFilter = new MmsProxyFilter(url);
|
||||
gpps.registerFilter(proxyFilter, 0);
|
||||
|
||||
let releaseMmsConnectionAndCallback = (function (httpStatus, data) {
|
||||
gpps.unregisterFilter(proxyFilter);
|
||||
// Always release the MMS network connection before callback.
|
||||
gMmsConnection.release();
|
||||
if (callback) {
|
||||
callback(httpStatus, data);
|
||||
}
|
||||
}).bind(this);
|
||||
cancellable.xhr = this.sendHttpRequest(method, url, istream, proxyFilter,
|
||||
cancellable.done.bind(cancellable));
|
||||
}).bind(this));
|
||||
|
||||
try {
|
||||
let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
|
||||
.createInstance(Ci.nsIXMLHttpRequest);
|
||||
return cancellable;
|
||||
},
|
||||
|
||||
// Basic setups
|
||||
xhr.open(method, url, true);
|
||||
xhr.responseType = "arraybuffer";
|
||||
if (istream) {
|
||||
xhr.setRequestHeader("Content-Type",
|
||||
"application/vnd.wap.mms-message");
|
||||
xhr.setRequestHeader("Content-Length", istream.available());
|
||||
}
|
||||
sendHttpRequest: function sendHttpRequest(method, url, istream, proxyFilter,
|
||||
callback) {
|
||||
let releaseMmsConnectionAndCallback = function (httpStatus, data) {
|
||||
gpps.unregisterFilter(proxyFilter);
|
||||
// Always release the MMS network connection before callback.
|
||||
gMmsConnection.release();
|
||||
callback(httpStatus, data);
|
||||
};
|
||||
|
||||
// UAProf headers.
|
||||
let uaProfUrl, uaProfTagname = "x-wap-profile";
|
||||
try {
|
||||
uaProfUrl = Services.prefs.getCharPref('wap.UAProf.url');
|
||||
uaProfTagname = Services.prefs.getCharPref('wap.UAProf.tagname');
|
||||
} catch (e) {}
|
||||
try {
|
||||
let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
|
||||
.createInstance(Ci.nsIXMLHttpRequest);
|
||||
|
||||
if (uaProfUrl) {
|
||||
xhr.setRequestHeader(uaProfTagname, uaProfUrl);
|
||||
}
|
||||
|
||||
// Setup event listeners
|
||||
xhr.onerror = function () {
|
||||
if (DEBUG) debug("xhr error, response headers: " +
|
||||
xhr.getAllResponseHeaders());
|
||||
releaseMmsConnectionAndCallback(xhr.status, null);
|
||||
};
|
||||
xhr.onreadystatechange = function () {
|
||||
if (xhr.readyState != Ci.nsIXMLHttpRequest.DONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
let data = null;
|
||||
switch (xhr.status) {
|
||||
case HTTP_STATUS_OK: {
|
||||
if (DEBUG) debug("xhr success, response headers: "
|
||||
+ xhr.getAllResponseHeaders());
|
||||
|
||||
let array = new Uint8Array(xhr.response);
|
||||
if (false) {
|
||||
for (let begin = 0; begin < array.length; begin += 20) {
|
||||
let partial = array.subarray(begin, begin + 20);
|
||||
if (DEBUG) debug("res: " + JSON.stringify(partial));
|
||||
}
|
||||
}
|
||||
|
||||
data = {array: array, offset: 0};
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
if (DEBUG) debug("xhr done, but status = " + xhr.status +
|
||||
", statusText = " + xhr.statusText);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
releaseMmsConnectionAndCallback(xhr.status, data);
|
||||
}
|
||||
|
||||
// Send request
|
||||
xhr.send(istream);
|
||||
} catch (e) {
|
||||
if (DEBUG) debug("xhr error, can't send: " + e.message);
|
||||
releaseMmsConnectionAndCallback(0, null);
|
||||
// Basic setups
|
||||
xhr.open(method, url, true);
|
||||
xhr.responseType = "arraybuffer";
|
||||
if (istream) {
|
||||
xhr.setRequestHeader("Content-Type",
|
||||
"application/vnd.wap.mms-message");
|
||||
xhr.setRequestHeader("Content-Length", istream.available());
|
||||
}
|
||||
}).bind(this, method, url, istream, callback));
|
||||
|
||||
// UAProf headers.
|
||||
let uaProfUrl, uaProfTagname = "x-wap-profile";
|
||||
try {
|
||||
uaProfUrl = Services.prefs.getCharPref('wap.UAProf.url');
|
||||
uaProfTagname = Services.prefs.getCharPref('wap.UAProf.tagname');
|
||||
} catch (e) {}
|
||||
|
||||
if (uaProfUrl) {
|
||||
xhr.setRequestHeader(uaProfTagname, uaProfUrl);
|
||||
}
|
||||
|
||||
// Setup event listeners
|
||||
xhr.onerror = function () {
|
||||
if (DEBUG) debug("xhr error, response headers: " +
|
||||
xhr.getAllResponseHeaders());
|
||||
releaseMmsConnectionAndCallback(xhr.status, null);
|
||||
};
|
||||
|
||||
xhr.onreadystatechange = function () {
|
||||
if (xhr.readyState != Ci.nsIXMLHttpRequest.DONE) {
|
||||
return;
|
||||
}
|
||||
let data = null;
|
||||
switch (xhr.status) {
|
||||
case HTTP_STATUS_OK: {
|
||||
if (DEBUG) debug("xhr success, response headers: "
|
||||
+ xhr.getAllResponseHeaders());
|
||||
let array = new Uint8Array(xhr.response);
|
||||
if (false) {
|
||||
for (let begin = 0; begin < array.length; begin += 20) {
|
||||
let partial = array.subarray(begin, begin + 20);
|
||||
if (DEBUG) debug("res: " + JSON.stringify(partial));
|
||||
}
|
||||
}
|
||||
|
||||
data = {array: array, offset: 0};
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
if (DEBUG) debug("xhr done, but status = " + xhr.status +
|
||||
", statusText = " + xhr.statusText);
|
||||
break;
|
||||
}
|
||||
}
|
||||
releaseMmsConnectionAndCallback(xhr.status, data);
|
||||
};
|
||||
// Send request
|
||||
xhr.send(istream);
|
||||
return xhr;
|
||||
} catch (e) {
|
||||
if (DEBUG) debug("xhr error, can't send: " + e.message);
|
||||
releaseMmsConnectionAndCallback(0, null);
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -586,6 +640,8 @@ XPCOMUtils.defineLazyGetter(this, "gMmsTransactionHelper", function () {
|
|||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
return helper;
|
||||
});
|
||||
|
||||
/**
|
||||
|
@ -634,47 +690,144 @@ NotifyResponseTransaction.prototype = {
|
|||
};
|
||||
|
||||
/**
|
||||
* Retrieve message back from MMSC.
|
||||
* CancellableTransaction - base class inherited by [Send|Retrieve]Transaction.
|
||||
* @param cancellableId
|
||||
* An ID used to keep track of if an message is deleted from DB.
|
||||
*/
|
||||
function CancellableTransaction(cancellableId) {
|
||||
this.cancellableId = cancellableId;
|
||||
this.isCancelled = false;
|
||||
}
|
||||
CancellableTransaction.prototype = {
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
|
||||
|
||||
// The timer for retrying sending or retrieving process.
|
||||
timer: null,
|
||||
|
||||
// Keep a reference to the callback when calling
|
||||
// |[Send|Retrieve]Transaction.run(callback)|.
|
||||
runCallback: null,
|
||||
|
||||
isObserversAdded: false,
|
||||
|
||||
registerRunCallback: function registerRunCallback(callback) {
|
||||
if (!this.isObserversAdded) {
|
||||
Services.obs.addObserver(this, kXpcomShutdownObserverTopic, false);
|
||||
Services.obs.addObserver(this, kMobileMessageDeletedObserverTopic, false);
|
||||
this.isObserversAdded = true;
|
||||
}
|
||||
|
||||
this.runCallback = callback;
|
||||
this.isCancelled = false;
|
||||
},
|
||||
|
||||
removeObservers: function removeObservers() {
|
||||
if (this.isObserversAdded) {
|
||||
Services.obs.removeObserver(this, kXpcomShutdownObserverTopic);
|
||||
Services.obs.removeObserver(this, kMobileMessageDeletedObserverTopic);
|
||||
this.isObserversAdded = false;
|
||||
}
|
||||
},
|
||||
|
||||
runCallbackIfValid: function runCallbackIfValid(mmsStatus, msg) {
|
||||
this.removeObservers();
|
||||
|
||||
if (this.runCallback) {
|
||||
this.runCallback(mmsStatus, msg);
|
||||
this.runCallback = null;
|
||||
}
|
||||
},
|
||||
|
||||
// Keep a reference to the cancellable when calling
|
||||
// |gMmsTransactionHelper.sendRequest(...)|.
|
||||
cancellable: null,
|
||||
|
||||
cancelRunning: function cancelRunning() {
|
||||
this.isCancelled = true;
|
||||
|
||||
if (this.timer) {
|
||||
// The sending or retrieving process is waiting for the next retry.
|
||||
// What we only need to do is to cancel the timer.
|
||||
this.timer.cancel();
|
||||
this.timer = null;
|
||||
this.runCallbackIfValid(_MMS_ERROR_MESSAGE_DELETED, null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.cancellable) {
|
||||
// The sending or retrieving process is still running. We attempt to
|
||||
// abort the HTTP request.
|
||||
this.cancellable.cancel();
|
||||
this.cancellable = null;
|
||||
}
|
||||
},
|
||||
|
||||
// nsIObserver
|
||||
|
||||
observe: function observe(subject, topic, data) {
|
||||
switch (topic) {
|
||||
case kXpcomShutdownObserverTopic: {
|
||||
this.cancelRunning();
|
||||
break;
|
||||
}
|
||||
case kMobileMessageDeletedObserverTopic: {
|
||||
data = JSON.parse(data);
|
||||
if (data.id != this.cancellableId) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.cancelRunning();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Class for retrieving message from MMSC, which inherits CancellableTransaction.
|
||||
*
|
||||
* @param contentLocation
|
||||
* X-Mms-Content-Location of the message.
|
||||
*/
|
||||
function RetrieveTransaction(contentLocation) {
|
||||
function RetrieveTransaction(cancellableId, contentLocation) {
|
||||
// Call |CancellableTransaction| constructor.
|
||||
CancellableTransaction.call(this, cancellableId);
|
||||
|
||||
this.contentLocation = contentLocation;
|
||||
}
|
||||
RetrieveTransaction.prototype = {
|
||||
/**
|
||||
* We need to keep a reference to the timer to assure the timer is fired.
|
||||
*/
|
||||
timer: null,
|
||||
|
||||
RetrieveTransaction.prototype = Object.create(CancellableTransaction.prototype, {
|
||||
/**
|
||||
* @param callback [optional]
|
||||
* A callback function that takes two arguments: one for X-Mms-Status,
|
||||
* the other for the parsed M-Retrieve.conf message.
|
||||
*/
|
||||
run: function run(callback) {
|
||||
this.retryCount = 0;
|
||||
let that = this;
|
||||
this.retrieve((function retryCallback(mmsStatus, msg) {
|
||||
if (MMS.MMS_PDU_STATUS_DEFERRED == mmsStatus &&
|
||||
that.retryCount < PREF_RETRIEVAL_RETRY_COUNT) {
|
||||
if (that.timer == null) {
|
||||
that.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
}
|
||||
run: {
|
||||
value: function run(callback) {
|
||||
this.registerRunCallback(callback);
|
||||
|
||||
that.timer.initWithCallback((function (){
|
||||
this.retrieve(retryCallback);
|
||||
}).bind(that),
|
||||
PREF_RETRIEVAL_RETRY_INTERVALS[that.retryCount],
|
||||
Ci.nsITimer.TYPE_ONE_SHOT);
|
||||
that.retryCount++;
|
||||
return;
|
||||
}
|
||||
if (callback) {
|
||||
callback(mmsStatus, msg);
|
||||
}
|
||||
}).bind(this));
|
||||
this.retryCount = 0;
|
||||
let that = this;
|
||||
this.retrieve((function retryCallback(mmsStatus, msg) {
|
||||
if (MMS.MMS_PDU_STATUS_DEFERRED == mmsStatus &&
|
||||
that.retryCount < PREF_RETRIEVAL_RETRY_COUNT) {
|
||||
if (that.timer == null) {
|
||||
that.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
}
|
||||
|
||||
that.timer.initWithCallback((function (){
|
||||
this.retrieve(retryCallback);
|
||||
}).bind(that),
|
||||
PREF_RETRIEVAL_RETRY_INTERVALS[that.retryCount],
|
||||
Ci.nsITimer.TYPE_ONE_SHOT);
|
||||
that.retryCount++;
|
||||
return;
|
||||
}
|
||||
this.runCallbackIfValid(mmsStatus, msg);
|
||||
}).bind(this));
|
||||
},
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -682,44 +835,59 @@ RetrieveTransaction.prototype = {
|
|||
* A callback function that takes two arguments: one for X-Mms-Status,
|
||||
* the other for the parsed M-Retrieve.conf message.
|
||||
*/
|
||||
retrieve: function retrieve(callback) {
|
||||
gMmsTransactionHelper.sendRequest("GET", this.contentLocation, null,
|
||||
(function (httpStatus, data) {
|
||||
if ((httpStatus != HTTP_STATUS_OK) || !data) {
|
||||
callback(MMS.MMS_PDU_STATUS_DEFERRED, null);
|
||||
return;
|
||||
}
|
||||
retrieve: {
|
||||
value: function retrieve(callback) {
|
||||
this.timer = null;
|
||||
|
||||
let retrieved = MMS.PduHelper.parse(data, null);
|
||||
if (!retrieved || (retrieved.type != MMS.MMS_PDU_TYPE_RETRIEVE_CONF)) {
|
||||
callback(MMS.MMS_PDU_STATUS_UNRECOGNISED, null);
|
||||
return;
|
||||
}
|
||||
this.cancellable =
|
||||
gMmsTransactionHelper.sendRequest("GET", this.contentLocation, null,
|
||||
(function (httpStatus, data) {
|
||||
if (httpStatus == _HTTP_STATUS_USER_CANCELLED) {
|
||||
callback(_MMS_ERROR_MESSAGE_DELETED, null);
|
||||
return;
|
||||
}
|
||||
|
||||
// Fix default header field values.
|
||||
if (retrieved.headers["x-mms-delivery-report"] == null) {
|
||||
retrieved.headers["x-mms-delivery-report"] = false;
|
||||
}
|
||||
if ((httpStatus != HTTP_STATUS_OK) || !data) {
|
||||
callback(MMS.MMS_PDU_STATUS_DEFERRED, null);
|
||||
return;
|
||||
}
|
||||
|
||||
let retrieveStatus = retrieved.headers["x-mms-retrieve-status"];
|
||||
if ((retrieveStatus != null) &&
|
||||
(retrieveStatus != MMS.MMS_PDU_ERROR_OK)) {
|
||||
callback(MMS.translatePduErrorToStatus(retrieveStatus),
|
||||
retrieved);
|
||||
return;
|
||||
}
|
||||
let retrieved = MMS.PduHelper.parse(data, null);
|
||||
if (!retrieved || (retrieved.type != MMS.MMS_PDU_TYPE_RETRIEVE_CONF)) {
|
||||
callback(MMS.MMS_PDU_STATUS_UNRECOGNISED, null);
|
||||
return;
|
||||
}
|
||||
|
||||
callback(MMS.MMS_PDU_STATUS_RETRIEVED, retrieved);
|
||||
}).bind(this));
|
||||
// Fix default header field values.
|
||||
if (retrieved.headers["x-mms-delivery-report"] == null) {
|
||||
retrieved.headers["x-mms-delivery-report"] = false;
|
||||
}
|
||||
|
||||
let retrieveStatus = retrieved.headers["x-mms-retrieve-status"];
|
||||
if ((retrieveStatus != null) &&
|
||||
(retrieveStatus != MMS.MMS_PDU_ERROR_OK)) {
|
||||
callback(MMS.translatePduErrorToStatus(retrieveStatus), retrieved);
|
||||
return;
|
||||
}
|
||||
|
||||
callback(MMS.MMS_PDU_STATUS_RETRIEVED, retrieved);
|
||||
}).bind(this));
|
||||
},
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* SendTransaction.
|
||||
* Class for sending M-Send.req to MMSC
|
||||
* Class for sending M-Send.req to MMSC, which inherits CancellableTransaction.
|
||||
* @throws Error("Check max values parameters fail.")
|
||||
*/
|
||||
function SendTransaction(msg) {
|
||||
function SendTransaction(cancellableId, msg) {
|
||||
// Call |CancellableTransaction| constructor.
|
||||
CancellableTransaction.call(this, cancellableId);
|
||||
|
||||
msg.headers["x-mms-message-type"] = MMS.MMS_PDU_TYPE_SEND_REQ;
|
||||
if (!msg.headers["x-mms-transaction-id"]) {
|
||||
// Create an unique transaction id
|
||||
|
@ -775,13 +943,13 @@ function SendTransaction(msg) {
|
|||
|
||||
this.msg = msg;
|
||||
}
|
||||
SendTransaction.prototype = {
|
||||
/**
|
||||
* We need to keep a reference to the timer to assure the timer is fired.
|
||||
*/
|
||||
timer: null,
|
||||
|
||||
istreamComposed: false,
|
||||
SendTransaction.prototype = Object.create(CancellableTransaction.prototype, {
|
||||
istreamComposed: {
|
||||
value: false,
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true
|
||||
},
|
||||
|
||||
/**
|
||||
* @param parts
|
||||
|
@ -789,42 +957,47 @@ SendTransaction.prototype = {
|
|||
* @param callback [optional]
|
||||
* A callback function that takes zero argument.
|
||||
*/
|
||||
loadBlobs: function loadBlobs(parts, callback) {
|
||||
let callbackIfValid = function callbackIfValid() {
|
||||
if (DEBUG) debug("All parts loaded: " + JSON.stringify(parts));
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
if (!parts || !parts.length) {
|
||||
callbackIfValid();
|
||||
return;
|
||||
}
|
||||
|
||||
let numPartsToLoad = parts.length;
|
||||
for each (let part in parts) {
|
||||
if (!(part.content instanceof Ci.nsIDOMBlob)) {
|
||||
numPartsToLoad--;
|
||||
if (!numPartsToLoad) {
|
||||
callbackIfValid();
|
||||
return;
|
||||
loadBlobs: {
|
||||
value: function loadBlobs(parts, callback) {
|
||||
let callbackIfValid = function callbackIfValid() {
|
||||
if (DEBUG) debug("All parts loaded: " + JSON.stringify(parts));
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
let fileReader = Cc["@mozilla.org/files/filereader;1"]
|
||||
.createInstance(Ci.nsIDOMFileReader);
|
||||
fileReader.addEventListener("loadend",
|
||||
(function onloadend(part, event) {
|
||||
let arrayBuffer = event.target.result;
|
||||
part.content = new Uint8Array(arrayBuffer);
|
||||
numPartsToLoad--;
|
||||
if (!numPartsToLoad) {
|
||||
callbackIfValid();
|
||||
|
||||
if (!parts || !parts.length) {
|
||||
callbackIfValid();
|
||||
return;
|
||||
}
|
||||
|
||||
let numPartsToLoad = parts.length;
|
||||
for each (let part in parts) {
|
||||
if (!(part.content instanceof Ci.nsIDOMBlob)) {
|
||||
numPartsToLoad--;
|
||||
if (!numPartsToLoad) {
|
||||
callbackIfValid();
|
||||
return;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}).bind(null, part));
|
||||
fileReader.readAsArrayBuffer(part.content);
|
||||
};
|
||||
let fileReader = Cc["@mozilla.org/files/filereader;1"]
|
||||
.createInstance(Ci.nsIDOMFileReader);
|
||||
fileReader.addEventListener("loadend",
|
||||
(function onloadend(part, event) {
|
||||
let arrayBuffer = event.target.result;
|
||||
part.content = new Uint8Array(arrayBuffer);
|
||||
numPartsToLoad--;
|
||||
if (!numPartsToLoad) {
|
||||
callbackIfValid();
|
||||
}
|
||||
}).bind(null, part));
|
||||
fileReader.readAsArrayBuffer(part.content);
|
||||
};
|
||||
},
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -832,47 +1005,54 @@ SendTransaction.prototype = {
|
|||
* A callback function that takes two arguments: one for
|
||||
* X-Mms-Response-Status, the other for the parsed M-Send.conf message.
|
||||
*/
|
||||
run: function run(callback) {
|
||||
if (!this.istreamComposed) {
|
||||
this.loadBlobs(this.msg.parts, (function () {
|
||||
this.istream = MMS.PduHelper.compose(null, this.msg);
|
||||
this.istreamComposed = true;
|
||||
this.run(callback);
|
||||
}).bind(this));
|
||||
return;
|
||||
}
|
||||
run: {
|
||||
value: function run(callback) {
|
||||
this.registerRunCallback(callback);
|
||||
|
||||
let callbackIfValid = function callbackIfValid(mmsStatus, msg) {
|
||||
if (callback) {
|
||||
callback(mmsStatus, msg);
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.istream) {
|
||||
callbackIfValid(MMS.MMS_PDU_ERROR_PERMANENT_FAILURE, null);
|
||||
return;
|
||||
}
|
||||
|
||||
this.retryCount = 0;
|
||||
let retryCallback = (function (mmsStatus, msg) {
|
||||
if ((MMS.MMS_PDU_ERROR_TRANSIENT_FAILURE == mmsStatus ||
|
||||
MMS.MMS_PDU_ERROR_PERMANENT_FAILURE == mmsStatus) &&
|
||||
this.retryCount < PREF_SEND_RETRY_COUNT) {
|
||||
if (this.timer == null) {
|
||||
this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
}
|
||||
|
||||
this.retryCount++;
|
||||
|
||||
this.timer.initWithCallback(this.send.bind(this, retryCallback),
|
||||
PREF_SEND_RETRY_INTERVAL,
|
||||
Ci.nsITimer.TYPE_ONE_SHOT);
|
||||
if (!this.istreamComposed) {
|
||||
this.loadBlobs(this.msg.parts, (function () {
|
||||
this.istream = MMS.PduHelper.compose(null, this.msg);
|
||||
this.istreamComposed = true;
|
||||
if (this.isCancelled) {
|
||||
this.runCallbackIfValid(_MMS_ERROR_MESSAGE_DELETED, null);
|
||||
} else {
|
||||
this.run(callback);
|
||||
}
|
||||
}).bind(this));
|
||||
return;
|
||||
}
|
||||
|
||||
callbackIfValid(mmsStatus, msg);
|
||||
}).bind(this);
|
||||
this.send(retryCallback);
|
||||
if (!this.istream) {
|
||||
this.runCallbackIfValid(MMS.MMS_PDU_ERROR_PERMANENT_FAILURE, null);
|
||||
return;
|
||||
}
|
||||
|
||||
this.retryCount = 0;
|
||||
let retryCallback = (function (mmsStatus, msg) {
|
||||
if ((MMS.MMS_PDU_ERROR_TRANSIENT_FAILURE == mmsStatus ||
|
||||
MMS.MMS_PDU_ERROR_PERMANENT_FAILURE == mmsStatus) &&
|
||||
this.retryCount < PREF_SEND_RETRY_COUNT) {
|
||||
if (this.timer == null) {
|
||||
this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
}
|
||||
|
||||
this.retryCount++;
|
||||
|
||||
this.timer.initWithCallback(this.send.bind(this, retryCallback),
|
||||
PREF_SEND_RETRY_INTERVAL,
|
||||
Ci.nsITimer.TYPE_ONE_SHOT);
|
||||
return;
|
||||
}
|
||||
|
||||
this.runCallbackIfValid(mmsStatus, msg);
|
||||
}).bind(this);
|
||||
|
||||
// This is the entry point to start sending.
|
||||
this.send(retryCallback);
|
||||
},
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -880,30 +1060,44 @@ SendTransaction.prototype = {
|
|||
* A callback function that takes two arguments: one for
|
||||
* X-Mms-Response-Status, the other for the parsed M-Send.conf message.
|
||||
*/
|
||||
send: function send(callback) {
|
||||
gMmsTransactionHelper.sendRequest("POST", gMmsConnection.mmsc, this.istream,
|
||||
function (httpStatus, data) {
|
||||
if (httpStatus != HTTP_STATUS_OK) {
|
||||
callback(MMS.MMS_PDU_ERROR_TRANSIENT_FAILURE, null);
|
||||
return;
|
||||
}
|
||||
send: {
|
||||
value: function send(callback) {
|
||||
this.timer = null;
|
||||
|
||||
if (!data) {
|
||||
callback(MMS.MMS_PDU_ERROR_PERMANENT_FAILURE, null);
|
||||
return;
|
||||
}
|
||||
this.cancellable =
|
||||
gMmsTransactionHelper.sendRequest("POST", gMmsConnection.mmsc,
|
||||
this.istream,
|
||||
function (httpStatus, data) {
|
||||
if (httpStatus == _HTTP_STATUS_USER_CANCELLED) {
|
||||
callback(_MMS_ERROR_MESSAGE_DELETED, null);
|
||||
return;
|
||||
}
|
||||
|
||||
let response = MMS.PduHelper.parse(data, null);
|
||||
if (!response || (response.type != MMS.MMS_PDU_TYPE_SEND_CONF)) {
|
||||
callback(MMS.MMS_PDU_RESPONSE_ERROR_UNSUPPORTED_MESSAGE, null);
|
||||
return;
|
||||
}
|
||||
if (httpStatus != HTTP_STATUS_OK) {
|
||||
callback(MMS.MMS_PDU_ERROR_TRANSIENT_FAILURE, null);
|
||||
return;
|
||||
}
|
||||
|
||||
let responseStatus = response.headers["x-mms-response-status"];
|
||||
callback(responseStatus, response);
|
||||
});
|
||||
if (!data) {
|
||||
callback(MMS.MMS_PDU_ERROR_PERMANENT_FAILURE, null);
|
||||
return;
|
||||
}
|
||||
|
||||
let response = MMS.PduHelper.parse(data, null);
|
||||
if (!response || (response.type != MMS.MMS_PDU_TYPE_SEND_CONF)) {
|
||||
callback(MMS.MMS_PDU_RESPONSE_ERROR_UNSUPPORTED_MESSAGE, null);
|
||||
return;
|
||||
}
|
||||
|
||||
let responseStatus = response.headers["x-mms-response-status"];
|
||||
callback(responseStatus, response);
|
||||
});
|
||||
},
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* Send M-acknowledge.ind back to MMSC.
|
||||
|
@ -1084,7 +1278,7 @@ MmsService.prototype = {
|
|||
// Notifying observers an MMS message is retrieving.
|
||||
Services.obs.notifyObservers(aDomMessage, kSmsRetrievingObserverTopic, null);
|
||||
|
||||
let transaction = new RetrieveTransaction(aContentLocation);
|
||||
let transaction = new RetrieveTransaction(aDomMessage.id, aContentLocation);
|
||||
transaction.run(aCallback);
|
||||
},
|
||||
|
||||
|
@ -1449,11 +1643,20 @@ MmsService.prototype = {
|
|||
|
||||
let self = this;
|
||||
|
||||
let sendTransactionCb = function sendTransactionCb(aRecordId, aErrorCode) {
|
||||
let sendTransactionCb = function sendTransactionCb(aDomMessage, aErrorCode) {
|
||||
if (DEBUG) debug("The error code of sending transaction: " + aErrorCode);
|
||||
|
||||
// If the messsage has been deleted (because the sending process is
|
||||
// cancelled), we don't need to reset the its delievery state/status.
|
||||
if (aErrorCode == Ci.nsIMobileMessageCallback.NOT_FOUND_ERROR) {
|
||||
aRequest.notifySendMessageFailed(aErrorCode);
|
||||
Services.obs.notifyObservers(aDomMessage, kSmsFailedObserverTopic, null);
|
||||
return;
|
||||
}
|
||||
|
||||
let isSentSuccess = (aErrorCode == Ci.nsIMobileMessageCallback.SUCCESS_NO_ERROR);
|
||||
gMobileMessageDatabaseService
|
||||
.setMessageDelivery(aRecordId,
|
||||
.setMessageDelivery(aDomMessage.id,
|
||||
null,
|
||||
isSentSuccess ? DELIVERY_SENT : DELIVERY_ERROR,
|
||||
isSentSuccess ? null : DELIVERY_STATUS_ERROR,
|
||||
|
@ -1488,14 +1691,16 @@ MmsService.prototype = {
|
|||
// For radio disabled error.
|
||||
if (gMmsConnection.radioDisabled) {
|
||||
if (DEBUG) debug("Error! Radio is disabled when sending MMS.");
|
||||
sendTransactionCb(aDomMessage.id, Ci.nsIMobileMessageCallback.RADIO_DISABLED_ERROR);
|
||||
sendTransactionCb(aDomMessage,
|
||||
Ci.nsIMobileMessageCallback.RADIO_DISABLED_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
// For SIM card is not ready.
|
||||
if (gRIL.rilContext.cardState != "ready") {
|
||||
if (DEBUG) debug("Error! SIM card is not ready when sending MMS.");
|
||||
sendTransactionCb(aDomMessage.id, Ci.nsIMobileMessageCallback.NO_SIM_CARD_ERROR);
|
||||
sendTransactionCb(aDomMessage,
|
||||
Ci.nsIMobileMessageCallback.NO_SIM_CARD_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1503,18 +1708,25 @@ MmsService.prototype = {
|
|||
Services.obs.notifyObservers(aDomMessage, kSmsSendingObserverTopic, null);
|
||||
let sendTransaction;
|
||||
try {
|
||||
sendTransaction = new SendTransaction(savableMessage);
|
||||
sendTransaction = new SendTransaction(aDomMessage.id, savableMessage);
|
||||
} catch (e) {
|
||||
if (DEBUG) debug("Exception: fail to create a SendTransaction instance.");
|
||||
sendTransactionCb(aDomMessage.id, Ci.nsIMobileMessageCallback.INTERNAL_ERROR);
|
||||
sendTransactionCb(aDomMessage,
|
||||
Ci.nsIMobileMessageCallback.INTERNAL_ERROR);
|
||||
return;
|
||||
}
|
||||
sendTransaction.run(function callback(aMmsStatus, aMsg) {
|
||||
let isSentSuccess = (aMmsStatus == MMS.MMS_PDU_ERROR_OK);
|
||||
if (DEBUG) debug("The sending status of sendTransaction.run(): " + aMmsStatus);
|
||||
sendTransactionCb(aDomMessage.id, isSentSuccess?
|
||||
Ci.nsIMobileMessageCallback.SUCCESS_NO_ERROR:
|
||||
Ci.nsIMobileMessageCallback.INTERNAL_ERROR);
|
||||
let errorCode;
|
||||
if (aMmsStatus == _MMS_ERROR_MESSAGE_DELETED) {
|
||||
errorCode = Ci.nsIMobileMessageCallback.NOT_FOUND_ERROR;
|
||||
} else if (aMmsStatus != MMS.MMS_PDU_ERROR_OK) {
|
||||
errorCode = Ci.nsIMobileMessageCallback.INTERNAL_ERROR;
|
||||
} else {
|
||||
errorCode = Ci.nsIMobileMessageCallback.SUCCESS_NO_ERROR;
|
||||
}
|
||||
|
||||
sendTransactionCb(aDomMessage, errorCode);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
@ -1571,6 +1783,13 @@ MmsService.prototype = {
|
|||
// For X-Mms-Report-Allowed
|
||||
let wish = aMessageRecord.headers["x-mms-delivery-report"];
|
||||
let responseNotify = function responseNotify(mmsStatus, retrievedMsg) {
|
||||
// If the messsage has been deleted (because the retrieving process is
|
||||
// cancelled), we don't need to reset the its delievery state/status.
|
||||
if (mmsStatus == _MMS_ERROR_MESSAGE_DELETED) {
|
||||
aRequest.notifyGetMessageFailed(Ci.nsIMobileMessageCallback.NOT_FOUND_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
// If the mmsStatus is still MMS_PDU_STATUS_DEFERRED after retry,
|
||||
// we should not store it into database and update its delivery
|
||||
// status to 'error'.
|
||||
|
|
|
@ -1542,7 +1542,11 @@ MobileMessageDatabaseService.prototype = {
|
|||
messageRecord.threadId,
|
||||
messageId,
|
||||
messageRecord.read);
|
||||
};
|
||||
|
||||
Services.obs.notifyObservers(null,
|
||||
"mobile-message-deleted",
|
||||
JSON.stringify({ id: messageId }));
|
||||
};
|
||||
} else if (DEBUG) {
|
||||
debug("Message id " + messageId + " does not exist");
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче