Merge b2g-inbound to m-c.

This commit is contained in:
Ryan VanderMeulen 2013-11-25 14:05:01 -05:00
commit daef4d0734
32 changed files with 832 additions and 364 deletions

View File

@ -18,4 +18,4 @@
# Modifying this file will now automatically clobber the buildbot machines \o/
#
Bug 941844 - Because Android resource builds are flaky.
Bug 921918 - need clobber for Windows.

View File

@ -1,4 +1,4 @@
{
"revision": "2a055e0d4214f04646c15c09a6a03bf96f347689",
"revision": "aac07cb753d6c364c3cbd39ad8116d455d5adf01",
"repo_path": "/integration/gaia-central"
}

View File

@ -771,6 +771,8 @@ GK_ATOM(onpopuphiding, "onpopuphiding")
GK_ATOM(onpopupshowing, "onpopupshowing")
GK_ATOM(onpopupshown, "onpopupshown")
GK_ATOM(onradiostatechange, "onradiostatechange")
GK_ATOM(onreaderror, "onreaderror")
GK_ATOM(onreadsuccess, "onreadsuccess")
GK_ATOM(onreadystatechange, "onreadystatechange")
GK_ATOM(onreceived, "onreceived")
GK_ATOM(onremoteheld, "onremoteheld")

View File

@ -79,11 +79,13 @@ class MediaRecorder::Session: public nsIObserver
MOZ_ASSERT(NS_IsMainThread());
MediaRecorder *recorder = mSession->mRecorder;
if (mSession->IsEncoderError()) {
recorder->NotifyError(NS_ERROR_UNEXPECTED);
}
nsresult rv = recorder->CreateAndDispatchBlobEvent(mSession);
if (NS_FAILED(rv)) {
recorder->NotifyError(rv);
}
return NS_OK;
}
@ -225,6 +227,13 @@ public:
return mEncodedBufferCache->ExtractBlob(mimeType);
}
bool IsEncoderError()
{
if (mEncoder && mEncoder->HasError()) {
return true;
}
return false;
}
private:
// Pull encoded meida data from MediaEncoder and put into EncodedBufferCache.

View File

@ -148,7 +148,7 @@ MediaEncoder::CreateEncoder(const nsAString& aMIMEType)
* If this is the last packet of input stream
* Set mState to ENCODE_DONE
*
* If mState is ENCODE_DONE
* If mState is ENCODE_DONE or ENCODE_ERROR
* Stop the loop
*/
void
@ -164,17 +164,23 @@ MediaEncoder::GetEncodedData(nsTArray<nsTArray<uint8_t> >* aOutputBufs,
switch (mState) {
case ENCODE_METADDATA: {
nsRefPtr<TrackMetadataBase> meta = mAudioEncoder->GetMetadata();
MOZ_ASSERT(meta);
if (meta == nullptr) {
LOG("ERROR! AudioEncoder get null Metadata!");
mState = ENCODE_ERROR;
break;
}
nsresult rv = mWriter->SetMetadata(meta);
if (NS_FAILED(rv)) {
mState = ENCODE_DONE;
LOG("ERROR! writer can't accept audio metadata!");
mState = ENCODE_ERROR;
break;
}
rv = mWriter->GetContainerData(aOutputBufs,
ContainerWriter::GET_HEADER);
if (NS_FAILED(rv)) {
mState = ENCODE_DONE;
LOG("ERROR! writer fail to generate header!");
mState = ENCODE_ERROR;
break;
}
@ -188,7 +194,7 @@ MediaEncoder::GetEncodedData(nsTArray<nsTArray<uint8_t> >* aOutputBufs,
if (NS_FAILED(rv)) {
// Encoding might be canceled.
LOG("ERROR! Fail to get encoded data from encoder.");
mState = ENCODE_DONE;
mState = ENCODE_ERROR;
break;
}
rv = mWriter->WriteEncodedTrack(encodedData,
@ -196,7 +202,7 @@ MediaEncoder::GetEncodedData(nsTArray<nsTArray<uint8_t> >* aOutputBufs,
ContainerWriter::END_OF_STREAM : 0);
if (NS_FAILED(rv)) {
LOG("ERROR! Fail to write encoded track to the media container.");
mState = ENCODE_DONE;
mState = ENCODE_ERROR;
break;
}
@ -217,7 +223,11 @@ MediaEncoder::GetEncodedData(nsTArray<nsTArray<uint8_t> >* aOutputBufs,
mShutdown = true;
reloop = false;
break;
case ENCODE_ERROR:
LOG("ERROR! MediaEncoder got error!");
mShutdown = true;
reloop = false;
break;
default:
MOZ_CRASH("Invalid encode state");
}

View File

@ -54,6 +54,7 @@ public :
ENCODE_METADDATA,
ENCODE_TRACK,
ENCODE_DONE,
ENCODE_ERROR,
};
MediaEncoder(ContainerWriter* aWriter,
@ -121,6 +122,11 @@ public :
}
}
bool HasError()
{
return mState == ENCODE_ERROR;
}
private:
nsAutoPtr<ContainerWriter> mWriter;
nsAutoPtr<AudioTrackEncoder> mAudioEncoder;

View File

@ -136,18 +136,17 @@ OpusTrackEncoder::~OpusTrackEncoder()
nsresult
OpusTrackEncoder::Init(int aChannels, int aSamplingRate)
{
// The track must have 1 or 2 channels.
if (aChannels <= 0 || aChannels > MAX_CHANNELS) {
LOG("[Opus] Fail to create the AudioTrackEncoder! The input has"
" %d channel(s), but expects no more than %d.", aChannels, MAX_CHANNELS);
return NS_ERROR_INVALID_ARG;
}
// This monitor is used to wake up other methods that are waiting for encoder
// to be completely initialized.
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
mChannels = aChannels;
// This version of encoder API only support 1 or 2 channels,
// So set the mChannels less or equal 2 and
// let InterleaveTrackData downmix pcm data.
mChannels = aChannels > 2 ? 2 : aChannels;
if (aChannels <= 0) {
return NS_ERROR_FAILURE;
}
// The granule position is required to be incremented at a rate of 48KHz, and
// it is simply calculated as |granulepos = samples * (48000/source_rate)|,
// that is, the source sampling rate must divide 48000 evenly.
@ -326,10 +325,6 @@ OpusTrackEncoder::GetEncodedTrack(EncodedFrameContainer& aData)
// encoding.
if (mSourceSegment->GetDuration() == 0 && mEndOfStream) {
mDoneEncoding = true;
if (mResampler) {
speex_resampler_destroy(mResampler);
mResampler = nullptr;
}
LOG("[Opus] Done encoding.");
}
@ -358,6 +353,12 @@ OpusTrackEncoder::GetEncodedTrack(EncodedFrameContainer& aData)
if (result < 0) {
LOG("[Opus] Fail to encode data! Result: %s.", opus_strerror(result));
}
if (mDoneEncoding) {
if (mResampler) {
speex_resampler_destroy(mResampler);
mResampler = nullptr;
}
}
audiodata->SetFrameData(&frameData);
aData.AppendEncodedFrame(audiodata);

View File

@ -141,8 +141,10 @@ protected:
uint32_t aOutputChannels, AudioDataValue* aOutput);
/**
* The number of channels in the first valid audio chunk, and is being used
* to initialize the audio encoder.
* The number of channels are used for processing PCM data in the audio encoder.
* This value comes from the first valid audio chunk. If encoder can't support
* the channels in the chunk, downmix PCM stream can be performed.
* This value also be used to initialize the audio encoder.
*/
int mChannels;
int mSamplingRate;

View File

@ -169,6 +169,7 @@ OggWriter::GetContainerData(nsTArray<nsTArray<uint8_t> >* aOutputBufs,
nsresult
OggWriter::SetMetadata(TrackMetadataBase* aMetadata)
{
MOZ_ASSERT(aMetadata);
if (aMetadata->GetKind() != TrackMetadataBase::METADATA_OPUS) {
LOG("wrong meta data type!");
return NS_ERROR_FAILURE;

View File

@ -237,6 +237,7 @@ support-files =
[test_mediarecorder_avoid_recursion.html]
[test_mediarecorder_record_timeslice.html]
[test_mediarecorder_record_audiocontext.html]
[test_mediarecorder_record_4ch_audiocontext.html]
[test_mediarecorder_record_stopms.html]
[test_mediarecorder_record_nosrc.html]
[test_mozHasAudio.html]

View File

@ -0,0 +1,74 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test MediaRecorder Record AudioContext with four channels</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
<script type="text/javascript" src="manifest.js"></script>
</head>
<body>
<script class="testbody" type="text/javascript">
function startTest() {
var context = new AudioContext();
var buffer = context.createBuffer(4, 80920, context.sampleRate);
for (var i = 0; i < 80920; ++i) {
for(var j = 0; j < 4; ++j) {
buffer.getChannelData(j)[i] = Math.sin(1000 * 2 * Math.PI * i / context.sampleRate);
}
}
var source = context.createBufferSource();
source.buffer = buffer;
var dest = context.createMediaStreamDestination();
var stopTriggered = false;
var onstopTriggered = false;
dest.channelCount = 4;
var expectedMimeType = 'audio/ogg';
source.channelCountMode = 'explicit';
source.connect(dest);
var elem = document.createElement('audio');
elem.mozSrcObject = dest.stream;
mMediaStream = dest.stream;
source.start(0);
elem.play();
mMediaRecorder = new MediaRecorder(dest.stream);
mMediaRecorder.onwarning = function() {
ok(false, 'onwarning unexpectedly fired');
};
mMediaRecorder.onerror = function() {
ok(false, 'onerror unexpectedly fired');
};
mMediaRecorder.onstop = function() {
ok(true, 'onstop fired successfully');
is(mMediaRecorder.state, 'inactive', 'check recording status is inactive');
onstopTriggered = true;
SimpleTest.finish();
};
mMediaRecorder.ondataavailable = function (e) {
ok(e.data.size > 0, 'check blob has data');
is(mMediaRecorder.mimeType, expectedMimeType, 'blob should has mimetype, return ' + mMediaRecorder.mimeType);
if (!stopTriggered) {
mMediaRecorder.stop();
stopTriggered = true;
} else if (onstopTriggered) {
ok(false, 'ondataavailable should come before onstop event');
}
};
try {
mMediaRecorder.start(1000);
is('recording', mMediaRecorder.state, "check record state recording");
} catch (e) {
ok(false, 'Can t record audio context');
}
}
startTest();
SimpleTest.waitForExplicitFinish();
</script>
</pre>
</body>
</html>

View File

@ -74,6 +74,9 @@ this.SystemMessagePermissionsTable = {
"sms-delivery-success": {
"sms": []
},
"sms-read-success": {
"sms": []
},
"sms-received": {
"sms": []
},

View File

@ -11,7 +11,7 @@ interface nsIDOMDOMCursor;
interface nsIDOMDOMRequest;
interface nsIDOMBlob;
[scriptable, builtinclass, uuid(cfcc7067-083f-4e09-91aa-75067a721b70)]
[scriptable, builtinclass, uuid(a99c3538-a8d6-492f-9ece-f6e92f9c00c5)]
interface nsIDOMMozMobileMessageManager : nsIDOMEventTarget
{
nsIDOMDOMRequest getSegmentInfoForText(in DOMString text);
@ -75,4 +75,6 @@ interface nsIDOMMozMobileMessageManager : nsIDOMEventTarget
[implicit_jscontext] attribute jsval onfailed;
[implicit_jscontext] attribute jsval ondeliverysuccess;
[implicit_jscontext] attribute jsval ondeliveryerror;
[implicit_jscontext] attribute jsval onreadsuccess;
[implicit_jscontext] attribute jsval onreaderror;
};

View File

@ -21,6 +21,9 @@ dictionary MmsDeliveryInfo
DOMString? deliveryStatus;
jsval deliveryTimestamp; // Date object; null if not available (e.g.,
// |delivery| = "received" or not yet delivered).
DOMString? readStatus;
jsval readTimestamp; // Date object. null if not available (e.g.,
// |delivery| = "received" or not yet read).
};
[scriptable, builtinclass, uuid(82ca2465-f967-4107-a4da-65b7a15d5dba)]

View File

@ -25,30 +25,27 @@ interface nsIRilMobileMessageDatabaseRecordCallback : nsISupports
void notify(in nsresult aRv, in jsval aMessageRecord, in nsISupports aDomMessage);
};
[scriptable, uuid(d5374151-7451-4590-a70e-40c49c1369ce)]
[scriptable, uuid(a92eba51-e619-4f70-98c5-175a33590582)]
interface nsIRilMobileMessageDatabaseService : nsIMobileMessageDatabaseService
{
/**
* |aMessage| Object: should contain the following properties for internal use:
* - |type| DOMString: "sms" or "mms"
* - |sender| DOMString: the phone number of sender
* - |timestamp| Number: the timestamp of received message
* - |iccId| DOMString: the ICC ID of the SIM for receiving message
* - |iccId| DOMString: [optional] the ICC ID of the SIM for receiving
* message if available.
*
* - If |type| == "sms", we also need:
* - |messageClass| DOMString: the message class of received message
* - |receiver| DOMString: the phone number of receiver
* - |pid| Number: the TP-PID field of the SMS TPDU, default 0.
* - |sender| DOMString: the phone number of sender
*
* - If |type| == "mms", we also need:
* - |delivery| DOMString: the delivery state of received message
* - |deliveryStatus| DOMString Array: the delivery status of received message
* - |deliveryStatus| DOMString: the delivery status of received message
* - |receivers| DOMString Array: the phone numbers of receivers
* - |phoneNumber| DOMString: [optional] my own phone number.
* - |transactionId| DOMString: the transaction ID from MMS PDU header.
*
* Note: |deliveryStatus| should only contain single string to specify
* the delivery status of MMS message for the phone owner self.
*/
void saveReceivedMessage(in jsval aMessage,
[optional] in nsIRilMobileMessageDatabaseCallback aCallback);
@ -88,15 +85,24 @@ interface nsIRilMobileMessageDatabaseService : nsIMobileMessageDatabaseService
/**
* |aEnvelopeId| DOMString: the "message-id" specified in the MMS PDU headers.
* |aReceiver| DOMString: the phone number of receiver (for MMS; can be null).
* |aDelivery| DOMString: the new delivery value to update (can be null).
* |aDeliveryStatus| DOMString: the new delivery status to update (can be null).
* |aDeliveryStatus| DOMString: the new delivery status to be updated (can be null).
* |aCallback| nsIRilMobileMessageDatabaseCallback: an optional callback.
*/
void setMessageDeliveryByEnvelopeId(in DOMString aEnvelopeId,
in DOMString aReceiver,
in DOMString aDelivery,
in DOMString aDeliveryStatus,
[optional] in nsIRilMobileMessageDatabaseCallback aCallback);
void setMessageDeliveryStatusByEnvelopeId(in DOMString aEnvelopeId,
in DOMString aReceiver,
in DOMString aDeliveryStatus,
[optional] in nsIRilMobileMessageDatabaseCallback aCallback);
/**
* |aEnvelopeId| DOMString: the "message-id" specified in the MMS PDU headers.
* |aReceiver| DOMString: the phone number of receiver (for MMS; can be null).
* |aReadStatus| DOMString: the new read status to be updated.
* |aCallback| nsIRilMobileMessageDatabaseCallback: an optional callback.
*/
void setMessageReadStatusByEnvelopeId(in DOMString aEnvelopeId,
in DOMString aReceiver,
in DOMString aReadStatus,
[optional] in nsIRilMobileMessageDatabaseCallback aCallback);
/**
* |aMessageId| Number: the message's DB record ID.

View File

@ -15,6 +15,8 @@ const char* kSmsFailedObserverTopic = "sms-failed";
const char* kSmsDeliverySuccessObserverTopic = "sms-delivery-success";
const char* kSmsDeliveryErrorObserverTopic = "sms-delivery-error";
const char* kSilentSmsReceivedObserverTopic = "silent-sms-received";
const char* kSmsReadSuccessObserverTopic = "sms-read-success";
const char* kSmsReadErrorObserverTopic = "sms-read-error";
} // namespace mobilemessage
} // namespace dom

View File

@ -19,6 +19,8 @@ extern const char* kSmsFailedObserverTopic;
extern const char* kSmsDeliverySuccessObserverTopic;
extern const char* kSmsDeliveryErrorObserverTopic;
extern const char* kSilentSmsReceivedObserverTopic;
extern const char* kSmsReadSuccessObserverTopic;
extern const char* kSmsReadErrorObserverTopic;
#define DELIVERY_RECEIVED NS_LITERAL_STRING("received")
#define DELIVERY_SENDING NS_LITERAL_STRING("sending")
@ -33,6 +35,11 @@ extern const char* kSilentSmsReceivedObserverTopic;
#define DELIVERY_STATUS_REJECTED NS_LITERAL_STRING("rejected")
#define DELIVERY_STATUS_MANUAL NS_LITERAL_STRING("manual")
#define READ_STATUS_NOT_APPLICABLE NS_LITERAL_STRING("not-applicable")
#define READ_STATUS_SUCCESS NS_LITERAL_STRING("success")
#define READ_STATUS_PENDING NS_LITERAL_STRING("pending")
#define READ_STATUS_ERROR NS_LITERAL_STRING("error")
#define MESSAGE_CLASS_NORMAL NS_LITERAL_STRING("normal")
#define MESSAGE_CLASS_CLASS_0 NS_LITERAL_STRING("class-0")
#define MESSAGE_CLASS_CLASS_1 NS_LITERAL_STRING("class-1")

View File

@ -145,6 +145,40 @@ MmsMessage::MmsMessage(const mobilemessage::MmsMessageData& aData)
}
}
// Prepare |info.readStatus|.
nsString statusReadString;
switch(infoData.readStatus()) {
case eReadStatus_NotApplicable:
statusReadString = READ_STATUS_NOT_APPLICABLE;
break;
case eReadStatus_Success:
statusReadString = READ_STATUS_SUCCESS;
break;
case eReadStatus_Pending:
statusReadString = READ_STATUS_PENDING;
break;
case eReadStatus_Error:
statusReadString = READ_STATUS_ERROR;
break;
case eReadStatus_EndGuard:
default:
MOZ_CRASH("We shouldn't get any other read status!");
}
info.readStatus = statusReadString;
// Prepare |info.readTimestamp|.
info.readTimestamp = JSVAL_NULL;
if (infoData.readTimestamp() != 0) {
AutoJSContext cx;
JS::Rooted<JSObject*>
dateObj(cx, JS_NewDateObjectMsec(cx, infoData.readTimestamp()));
if (!dateObj) {
NS_WARNING("MmsMessage: Unable to create Data for readTimestamp.");
} else {
info.readTimestamp = OBJECT_TO_JSVAL(dateObj);
}
}
mDeliveryInfo.AppendElement(info);
}
}
@ -376,6 +410,29 @@ MmsMessage::GetData(ContentParent* aParent,
convertTimeToInt(cx, info.deliveryTimestamp, infoData.deliveryTimestamp());
}
// Prepare |infoData.readStatus|.
ReadStatus readStatus;
if (info.readStatus.Equals(READ_STATUS_NOT_APPLICABLE)) {
readStatus = eReadStatus_NotApplicable;
} else if (info.readStatus.Equals(READ_STATUS_SUCCESS)) {
readStatus = eReadStatus_Success;
} else if (info.readStatus.Equals(READ_STATUS_PENDING)) {
readStatus = eReadStatus_Pending;
} else if (info.readStatus.Equals(READ_STATUS_ERROR)) {
readStatus = eReadStatus_Error;
} else {
return false;
}
infoData.readStatus() = readStatus;
// Prepare |infoData.readTimestamp|.
if (info.readTimestamp == JSVAL_NULL) {
infoData.readTimestamp() = 0;
} else {
AutoJSContext cx;
convertTimeToInt(cx, info.readTimestamp, infoData.readTimestamp());
}
aData.deliveryInfo().AppendElement(infoData);
}
@ -521,6 +578,24 @@ MmsMessage::GetDeliveryInfo(JSContext* aCx, JS::Value* aDeliveryInfo)
return NS_ERROR_FAILURE;
}
// Get |info.readStatus|.
tmpJsStr = JS_NewUCStringCopyN(aCx,
info.readStatus.get(),
info.readStatus.Length());
NS_ENSURE_TRUE(tmpJsStr, NS_ERROR_OUT_OF_MEMORY);
tmpJsVal.setString(tmpJsStr);
if (!JS_DefineProperty(aCx, infoJsObj, "readStatus", tmpJsVal,
NULL, NULL, JSPROP_ENUMERATE)) {
return NS_ERROR_FAILURE;
}
// Get |info.readTimestamp|.
if (!JS_DefineProperty(aCx, infoJsObj, "readTimestamp", info.readTimestamp,
NULL, NULL, JSPROP_ENUMERATE)) {
return NS_ERROR_FAILURE;
}
tmpJsVal = OBJECT_TO_JSVAL(infoJsObj);
if (!JS_SetElement(aCx, deliveryInfo, i, &tmpJsVal)) {
return NS_ERROR_FAILURE;

View File

@ -38,6 +38,8 @@
#define FAILED_EVENT_NAME NS_LITERAL_STRING("failed")
#define DELIVERY_SUCCESS_EVENT_NAME NS_LITERAL_STRING("deliverysuccess")
#define DELIVERY_ERROR_EVENT_NAME NS_LITERAL_STRING("deliveryerror")
#define READ_SUCCESS_EVENT_NAME NS_LITERAL_STRING("readsuccess")
#define READ_ERROR_EVENT_NAME NS_LITERAL_STRING("readerror")
using namespace mozilla::dom::mobilemessage;
@ -62,6 +64,8 @@ NS_IMPL_EVENT_HANDLER(MobileMessageManager, sent)
NS_IMPL_EVENT_HANDLER(MobileMessageManager, failed)
NS_IMPL_EVENT_HANDLER(MobileMessageManager, deliverysuccess)
NS_IMPL_EVENT_HANDLER(MobileMessageManager, deliveryerror)
NS_IMPL_EVENT_HANDLER(MobileMessageManager, readsuccess)
NS_IMPL_EVENT_HANDLER(MobileMessageManager, readerror)
void
MobileMessageManager::Init(nsPIDOMWindow *aWindow)
@ -81,6 +85,8 @@ MobileMessageManager::Init(nsPIDOMWindow *aWindow)
obs->AddObserver(this, kSmsFailedObserverTopic, false);
obs->AddObserver(this, kSmsDeliverySuccessObserverTopic, false);
obs->AddObserver(this, kSmsDeliveryErrorObserverTopic, false);
obs->AddObserver(this, kSmsReadSuccessObserverTopic, false);
obs->AddObserver(this, kSmsReadErrorObserverTopic, false);
}
void
@ -99,6 +105,8 @@ MobileMessageManager::Shutdown()
obs->RemoveObserver(this, kSmsFailedObserverTopic);
obs->RemoveObserver(this, kSmsDeliverySuccessObserverTopic);
obs->RemoveObserver(this, kSmsDeliveryErrorObserverTopic);
obs->RemoveObserver(this, kSmsReadSuccessObserverTopic);
obs->RemoveObserver(this, kSmsReadErrorObserverTopic);
}
NS_IMETHODIMP
@ -536,6 +544,14 @@ MobileMessageManager::Observe(nsISupports* aSubject, const char* aTopic,
return DispatchTrustedSmsEventToSelf(aTopic, DELIVERY_ERROR_EVENT_NAME, aSubject);
}
if (!strcmp(aTopic, kSmsReadSuccessObserverTopic)) {
return DispatchTrustedSmsEventToSelf(aTopic, READ_SUCCESS_EVENT_NAME, aSubject);
}
if (!strcmp(aTopic, kSmsReadErrorObserverTopic)) {
return DispatchTrustedSmsEventToSelf(aTopic, READ_ERROR_EVENT_NAME, aSubject);
}
return NS_OK;
}

View File

@ -38,6 +38,16 @@ enum DeliveryStatus {
eDeliveryStatus_EndGuard
};
// For MmsMessageData.readStatus.
enum ReadStatus {
eReadStatus_NotApplicable = 0,
eReadStatus_Success,
eReadStatus_Pending,
eReadStatus_Error,
// This state should stay at the end.
eReadStatus_EndGuard
};
// For {Mms,Sms}FilterData.read.
enum ReadState {
eReadState_Unknown = -1,
@ -92,6 +102,16 @@ struct ParamTraits<mozilla::dom::mobilemessage::DeliveryStatus>
mozilla::dom::mobilemessage::eDeliveryStatus_EndGuard>
{};
/**
* Read status serializer.
*/
template <>
struct ParamTraits<mozilla::dom::mobilemessage::ReadStatus>
: public EnumSerializer<mozilla::dom::mobilemessage::ReadStatus,
mozilla::dom::mobilemessage::eReadStatus_NotApplicable,
mozilla::dom::mobilemessage::eReadStatus_EndGuard>
{};
/**
* Read state serializer.
*/

View File

@ -45,6 +45,48 @@ function defineLazyRegExp(obj, name, pattern) {
});
}
function RangedValue(name, min, max) {
this.name = name;
this.min = min;
this.max = max;
}
RangedValue.prototype = {
name: null,
min: null,
max: null,
/**
* @param data
* A wrapped object containing raw PDU data.
*
* @return A decoded integer.
*
* @throws CodeError if decoded value is not in the range [this.min, this.max].
*/
decode: function decode(data) {
let value = WSP.Octet.decode(data);
if ((value >= this.min) && (value <= this.max)) {
return value;
}
throw new WSP.CodeError(this.name + ": invalid value " + value);
},
/**
* @param data
* A wrapped object to store encoded raw data.
* @param value
* An integer value within thr range [this.min, this.max].
*/
encode: function encode(data, value) {
if ((value < this.min) || (value > this.max)) {
throw new WSP.CodeError(this.name + ": invalid value " + value);
}
WSP.Octet.encode(data, value);
},
};
/**
* Internal decoding function for boolean values.
*
@ -337,44 +379,21 @@ this.MmsHeader = {
},
};
/**
* Cancel-status-value = Cancel Request Successfully received |
* Cancel Request corrupted
*
* @see OMA-TS-MMS_ENC-V1_3-20110913-A clause 7.3.7
*/
this.CancelStatusValue = new RangedValue("Cancel-status-value", 128, 129);
/**
* Content-class-value = text | image-basic| image-rich | video-basic |
* video-rich | megapixel | content-basic | content-rich
*
* @see OMA-TS-MMS_ENC-V1_3-20110913-A clause 7.3.9
*/
this.ContentClassValue = {
/**
* @param data
* A wrapped object containing raw PDU data.
*
* @return A integer value for each class.
*
* @throws CodeError if decoded value is not in range 128..135.
*/
decode: function decode(data) {
let value = WSP.Octet.decode(data);
if ((value >= 128) && (value <= 135)) {
return value;
}
throw new WSP.CodeError("Content-class-value: invalid class " + value);
},
/**
* @param data
* A wrapped object to store encoded raw data.
* @param value
* A numeric content class value to be encoded.
*/
encode: function encode(data, value) {
if ((value < 128) || (value > 135)) {
throw new WSP.CodeError("Content-class-value: invalid class " + value);
}
WSP.Octet.encode(data, value);
},
};
this.ContentClassValue = new RangedValue("Content-class-value", 128, 135);
/**
* When used in a PDU other than M-Mbox-Delete.conf and M-Delete.conf:
@ -969,40 +988,7 @@ this.MessageClassValue = {
*
* @see OMA-TS-MMS_ENC-V1_3-20110913-A clause 7.3.30
*/
this.MessageTypeValue = {
/**
* @param data
* A wrapped object containing raw PDU data.
*
* @return A decoded integer.
*
* @throws CodeError if decoded value is not in the range 128..151.
*/
decode: function decode(data) {
let type = WSP.Octet.decode(data);
if ((type >= 128) && (type <= 151)) {
return type;
}
throw new WSP.CodeError("Message-type-value: invalid type " + type);
},
/**
* @param data
* A wrapped object to store encoded raw data.
* @param type
* A numeric message type value to be encoded.
*
* @throws CodeError if the value is not in the range 128..151.
*/
encode: function encode(data, type) {
if ((type < 128) || (type > 151)) {
throw new WSP.CodeError("Message-type-value: invalid type " + type);
}
WSP.Octet.encode(data, type);
},
};
this.MessageTypeValue = new RangedValue("Message-type-value", 128, 151);
/**
* MM-flags-value = Value-length ( Add-token | Remove-token | Filter-token ) Encoded-string-value
@ -1075,40 +1061,7 @@ this.MmFlagsValue = {
*
* @see OMA-TS-MMS_ENC-V1_3-20110913-A clause 7.3.33
*/
this.MmStateValue = {
/**
* @param data
* A wrapped object containing raw PDU data.
*
* @return A decoded integer.
*
* @throws CodeError if decoded value is not in the range 128..132.
*/
decode: function decode(data) {
let state = WSP.Octet.decode(data);
if ((state >= 128) && (state <= 132)) {
return state;
}
throw new WSP.CodeError("MM-state-value: invalid state " + state);
},
/**
* @param data
* A wrapped object to store encoded raw data.
* @param state
* A numeric state value to be encoded.
*
* @throws CodeError if state is not in the range 128..132.
*/
encode: function encode(data, state) {
if ((state < 128) || (state > 132)) {
throw new WSP.CodeError("MM-state-value: invalid state " + state);
}
WSP.Octet.encode(data, state);
},
};
this.MmStateValue = new RangedValue("MM-state-value", 128, 132);
/**
* Priority-value = Low | Normal | High
@ -1118,38 +1071,14 @@ this.MmStateValue = {
*
* @see OMA-TS-MMS_ENC-V1_3-20110913-A clause 7.3.35
*/
this.PriorityValue = {
/**
* @param data
* A wrapped object containing raw PDU data.
*
* @return A decoded integer.
*
* @throws CodeError if decoded value is not in the range 128..130.
*/
decode: function decode(data) {
let priority = WSP.Octet.decode(data);
if ((priority >= 128) && (priority <= 130)) {
return priority;
}
this.PriorityValue = new RangedValue("Priority-value", 128, 130);
throw new WSP.CodeError("Priority-value: invalid priority " + priority);
},
/**
* @param data
* A wrapped object to store encoded raw data.
* @param priority
* A numeric priority value to be encoded.
*/
encode: function encode(data, priority) {
if ((priority < 128) || (priority > 130)) {
throw new WSP.CodeError("Priority-value: invalid priority " + priority);
}
WSP.Octet.encode(data, priority);
},
};
/**
* Read-status-value = Read | Deleted without being read
*
* @see OMA-TS-MMS_ENC-V1_3-20110913-A clause 7.3.38
*/
this.ReadStatusValue = new RangedValue("Read-status-value", 128, 129);
/**
* Recommended-Retrieval-Mode-value = Manual
@ -1179,38 +1108,7 @@ this.RecommendedRetrievalModeValue = {
*
* @see OMA-TS-MMS_ENC-V1_3-20110913-A clause 7.3.43
*/
this.ReplyChargingValue = {
/**
* @param data
* A wrapped object containing raw PDU data.
*
* @return A decoded integer.
*
* @throws CodeError if decoded value is not in the range 128..131.
*/
decode: function decode(data) {
let value = WSP.Octet.decode(data);
if ((value >= 128) && (value <= 131)) {
return value;
}
throw new WSP.CodeError("Reply-charging-value: invalid value " + value);
},
/**
* @param data
* A wrapped object to store encoded raw data.
* @param value
* An integer value within thr range 128..131.
*/
encode: function encode(data, value) {
if ((value < 128) || (value > 131)) {
throw new WSP.CodeError("Reply-charging-value: invalid value " + value);
}
WSP.Octet.encode(data, value);
},
};
this.ReplyChargingValue = new RangedValue("Reply-charging-value", 128, 131);
/**
* When used in a PDU other than M-Mbox-Delete.conf and M-Delete.conf:
@ -1299,6 +1197,13 @@ this.RetrieveStatusValue = {
},
};
/**
* Sender-visibility-value = Hide | Show
*
* @see OMA-TS-MMS_ENC-V1_3-20110913-A clause 7.3.52
*/
this.SenderVisibilityValue = new RangedValue("Sender-visibility-value", 128, 129);
/**
* Status-value = Expired | Retrieved | Rejected | Deferred | Unrecognised |
* Indeterminate | Forwarded | Unreachable
@ -1313,40 +1218,7 @@ this.RetrieveStatusValue = {
*
* @see OMA-TS-MMS_ENC-V1_3-20110913-A clause 7.3.54
*/
this.StatusValue = {
/**
* @param data
* A wrapped object containing raw PDU data.
*
* @return A decoded integer.
*
* @throws CodeError if decoded value is not in the range 128..135.
*/
decode: function decode(data) {
let status = WSP.Octet.decode(data);
if ((status >= 128) && (status <= 135)) {
return status;
}
throw new WSP.CodeError("Status-value: invalid status " + status);
},
/**
* @param data
* A wrapped object to store encoded raw data.
* @param value
* A numeric status value to be encoded.
*
* @throws CodeError if the value is not in the range 128..135.
*/
encode: function encode(data, value) {
if ((value < 128) || (value > 135)) {
throw new WSP.CodeError("Status-value: invalid status " + value);
}
WSP.Octet.encode(data, value);
},
};
this.StatusValue = new RangedValue("Status-value", 128, 135);
this.PduHelper = {
/**
@ -1632,6 +1504,13 @@ const MMS_PDU_TYPES = (function () {
"to",
"from",
"x-mms-read-status"]);
add(MMS_PDU_TYPE_READ_ORIG_IND, false, ["x-mms-message-type",
"x-mms-mms-version",
"message-id",
"to",
"from",
"date",
"x-mms-read-status"]);
return pdus;
})();
@ -1671,14 +1550,14 @@ const MMS_HEADER_FIELDS = (function () {
add("x-mms-report-allowed", 0x11, BooleanValue);
add("x-mms-response-status", 0x12, RetrieveStatusValue);
add("x-mms-response-text", 0x13, ResponseText);
add("x-mms-sender-visibility", 0x14, BooleanValue);
add("x-mms-sender-visibility", 0x14, SenderVisibilityValue);
add("x-mms-status", 0x15, StatusValue);
add("subject", 0x16, EncodedStringValue);
add("to", 0x17, Address);
add("x-mms-transaction-id", 0x18, WSP.TextString);
add("x-mms-retrieve-status", 0x19, RetrieveStatusValue);
add("x-mms-retrieve-text", 0x1A, EncodedStringValue);
add("x-mms-read-status", 0x1B, BooleanValue);
add("x-mms-read-status", 0x1B, ReadStatusValue);
add("x-mms-reply-charging", 0x1C, ReplyChargingValue);
add("x-mms-reply-charging-deadline", 0x1D, ExpiryValue);
add("x-mms-reply-charging-id", 0x1E, WSP.TextString);
@ -1714,7 +1593,7 @@ const MMS_HEADER_FIELDS = (function () {
add("x-mms-adaptation-allowed", 0x3C, BooleanValue);
add("x-mms-replace-id", 0x3D, WSP.TextString);
add("x-mms-cancel-id", 0x3E, WSP.TextString);
add("x-mms-cancel-status", 0x3F, BooleanValue);
add("x-mms-cancel-status", 0x3F, CancelStatusValue);
return names;
})();
@ -1759,6 +1638,7 @@ this.EXPORTED_SYMBOLS = ALL_CONST_SYMBOLS.concat([
"Address",
"HeaderField",
"MmsHeader",
"CancelStatusValue",
"ContentClassValue",
"ContentLocationValue",
"ElementDescriptorValue",
@ -1773,10 +1653,12 @@ this.EXPORTED_SYMBOLS = ALL_CONST_SYMBOLS.concat([
"MmFlagsValue",
"MmStateValue",
"PriorityValue",
"ReadStatusValue",
"RecommendedRetrievalModeValue",
"ReplyChargingValue",
"ResponseText",
"RetrieveStatusValue",
"SenderVisibilityValue",
"StatusValue",
// Parser

View File

@ -18,6 +18,9 @@ const RIL_MMSSERVICE_CONTRACTID = "@mozilla.org/mms/rilmmsservice;1";
const RIL_MMSSERVICE_CID = Components.ID("{217ddd76-75db-4210-955d-8806cd8d87f9}");
let DEBUG = false;
function debug(s) {
dump("-@- MmsService: " + s + "\n");
};
// Read debug setting from pref.
try {
@ -32,6 +35,8 @@ const kSmsReceivedObserverTopic = "sms-received";
const kSmsRetrievingObserverTopic = "sms-retrieving";
const kSmsDeliverySuccessObserverTopic = "sms-delivery-success";
const kSmsDeliveryErrorObserverTopic = "sms-delivery-error";
const kSmsReadSuccessObserverTopic = "sms-read-success";
const kSmsReadErrorObserverTopic = "sms-read-error";
const NS_XPCOM_SHUTDOWN_OBSERVER_ID = "xpcom-shutdown";
const kNetworkInterfaceStateChangedTopic = "network-interface-state-changed";
@ -1354,7 +1359,7 @@ function ReadRecTransaction(mmsConnection, messageID, toAddress) {
type: type}
headers["to"] = to;
headers["from"] = null;
headers["x-mms-read-status"] = true;
headers["x-mms-read-status"] = MMS.MMS_PDU_READ_STATUS_READ;
this.istream = MMS.PduHelper.compose(null, {headers: headers});
if (!this.istream) {
@ -1431,38 +1436,33 @@ MmsService.prototype = {
retrievalMode) {
intermediate.type = "mms";
intermediate.delivery = DELIVERY_NOT_DOWNLOADED;
// As a receiver, we don't need to care about the delivery status of others.
let deliveryInfo = intermediate.deliveryInfo = [{
receiver: mmsConnection.getPhoneNumber(),
deliveryStatus: DELIVERY_STATUS_NOT_APPLICABLE }];
let deliveryStatus;
switch (retrievalMode) {
case RETRIEVAL_MODE_MANUAL:
deliveryInfo[0].deliveryStatus = DELIVERY_STATUS_MANUAL;
deliveryStatus = DELIVERY_STATUS_MANUAL;
break;
case RETRIEVAL_MODE_NEVER:
deliveryInfo[0].deliveryStatus = DELIVERY_STATUS_REJECTED;
deliveryStatus = DELIVERY_STATUS_REJECTED;
break;
case RETRIEVAL_MODE_AUTOMATIC:
deliveryInfo[0].deliveryStatus = DELIVERY_STATUS_PENDING;
deliveryStatus = DELIVERY_STATUS_PENDING;
break;
case RETRIEVAL_MODE_AUTOMATIC_HOME:
if (mmsConnection.isVoiceRoaming()) {
deliveryInfo[0].deliveryStatus = DELIVERY_STATUS_MANUAL;
deliveryStatus = DELIVERY_STATUS_MANUAL;
} else {
deliveryInfo[0].deliveryStatus = DELIVERY_STATUS_PENDING;
deliveryStatus = DELIVERY_STATUS_PENDING;
}
break;
default:
deliveryStatus = DELIVERY_STATUS_NOT_APPLICABLE;
break;
}
// |intermediate.deliveryStatus| will be deleted after being stored in db.
intermediate.deliveryStatus = deliveryStatus;
intermediate.timestamp = Date.now();
intermediate.sender = null;
intermediate.transactionId = intermediate.headers["x-mms-transaction-id"];
if (intermediate.headers.from) {
intermediate.sender = intermediate.headers.from.address;
} else {
intermediate.sender = "anonymous";
}
intermediate.receivers = [];
intermediate.phoneNumber = mmsConnection.getPhoneNumber();
intermediate.iccId = mmsConnection.getIccId();
@ -1485,11 +1485,6 @@ MmsService.prototype = {
intermediate,
savable) {
savable.timestamp = Date.now();
if (intermediate.headers.from) {
savable.sender = intermediate.headers.from.address;
} else {
savable.sender = "anonymous";
}
savable.receivers = [];
// We don't have Bcc in recevied MMS message.
for each (let type in ["cc", "to"]) {
@ -1505,10 +1500,8 @@ MmsService.prototype = {
}
savable.delivery = DELIVERY_RECEIVED;
// As a receiver, we don't need to care about the delivery status of others.
savable.deliveryInfo = [{
receiver: mmsConnection.getPhoneNumber(),
deliveryStatus: DELIVERY_STATUS_SUCCESS }];
// |savable.deliveryStatus| will be deleted after being stored in db.
savable.deliveryStatus = DELIVERY_STATUS_SUCCESS;
for (let field in intermediate.headers) {
savable.headers[field] = intermediate.headers[field];
}
@ -1615,6 +1608,8 @@ MmsService.prototype = {
retrievedMessage) {
if (DEBUG) debug("retrievedMessage = " + JSON.stringify(retrievedMessage));
let transactionId = savableMessage.headers["x-mms-transaction-id"];
// The absence of the field does not indicate any default
// value. So we go check the same field in the retrieved
// message instead.
@ -1654,8 +1649,6 @@ MmsService.prototype = {
savableMessage = this.mergeRetrievalConfirmation(mmsConnection,
retrievedMessage,
savableMessage);
let transactionId = savableMessage.headers["x-mms-transaction-id"];
gMobileMessageDatabaseService.saveReceivedMessage(savableMessage,
(function (rv, domMessage) {
let success = Components.isSuccessCode(rv);
@ -1805,11 +1798,6 @@ MmsService.prototype = {
* The MMS message object.
*/
handleDeliveryIndication: function handleDeliveryIndication(aMsg) {
if (DEBUG) {
debug("handleDeliveryIndication: got delivery report" +
JSON.stringify(aMsg));
}
let headers = aMsg.headers;
let envelopeId = headers["message-id"];
let address = headers.to.address;
@ -1847,11 +1835,8 @@ MmsService.prototype = {
if (DEBUG) debug("Updating the delivery status to: " + deliveryStatus);
gMobileMessageDatabaseService
.setMessageDeliveryByEnvelopeId(envelopeId,
address,
null,
deliveryStatus,
(function notifySetDeliveryResult(aRv, aDomMessage) {
.setMessageDeliveryStatusByEnvelopeId(envelopeId, address, deliveryStatus,
(function(aRv, aDomMessage) {
if (DEBUG) debug("Marking the delivery status is done.");
// TODO bug 832140 handle !Components.isSuccessCode(aRv)
@ -1873,6 +1858,57 @@ MmsService.prototype = {
}).bind(this));
},
/**
* Handle incoming M-Read-Orig.ind PDU.
*
* @param aIndication
* The MMS message object.
*/
handleReadOriginateIndication:
function handleReadOriginateIndication(aIndication) {
let headers = aIndication.headers;
let envelopeId = headers["message-id"];
let address = headers.from.address;
let mmsReadStatus = headers["x-mms-read-status"];
if (DEBUG) {
debug("Start updating the read status for envelopeId: " + envelopeId +
", address: " + address + ", mmsReadStatus: " + mmsReadStatus);
}
// From OMA-TS-MMS_ENC-V1_3-20110913-A subclause 9.4 "X-Mms-Read-Status",
// in M-Read-Rec-Orig.ind the X-Mms-Read-Status could be
// MMS.MMS_READ_STATUS_{ READ, DELETED_WITHOUT_BEING_READ }.
let readStatus = mmsReadStatus == MMS.MMS_PDU_READ_STATUS_READ
? MMS.DOM_READ_STATUS_SUCCESS
: MMS.DOM_READ_STATUS_ERROR;
if (DEBUG) debug("Updating the read status to: " + readStatus);
gMobileMessageDatabaseService
.setMessageReadStatusByEnvelopeId(envelopeId, address, readStatus,
(function(aRv, aDomMessage) {
if (!Components.isSuccessCode(aRv)) {
// Notifying observers the read status is error.
Services.obs.notifyObservers(aDomMessage, kSmsReadSuccessObserverTopic, null);
return;
}
if (DEBUG) debug("Marking the read status is done.");
let topic;
if (mmsReadStatus == MMS.MMS_PDU_READ_STATUS_READ) {
topic = kSmsReadSuccessObserverTopic;
// Broadcasting a 'sms-read-success' system message to open apps.
this.broadcastMmsSystemMessage(topic, aDomMessage);
} else {
topic = kSmsReadErrorObserverTopic;
}
// Notifying observers the read status is updated.
Services.obs.notifyObservers(aDomMessage, topic, null);
}).bind(this));
},
/**
* A utility function to convert the MmsParameters dictionary object
* to a database-savable message.
@ -2113,7 +2149,7 @@ MmsService.prototype = {
if (errorCode !== Ci.nsIMobileMessageCallback.SUCCESS_NO_ERROR) {
if (DEBUG) debug("Error! The params for sending MMS are invalid.");
sendTransactionCb(aDomMessage, errorCode);
sendTransactionCb(aDomMessage, errorCode, null);
return;
}
@ -2126,7 +2162,7 @@ MmsService.prototype = {
} catch (e) {
if (DEBUG) debug("Exception: fail to create a SendTransaction instance.");
sendTransactionCb(aDomMessage,
Ci.nsIMobileMessageCallback.INTERNAL_ERROR);
Ci.nsIMobileMessageCallback.INTERNAL_ERROR, null);
return;
}
sendTransaction.run(function callback(aMmsStatus, aMsg) {
@ -2143,10 +2179,8 @@ MmsService.prototype = {
} else {
errorCode = Ci.nsIMobileMessageCallback.SUCCESS_NO_ERROR;
}
let envelopeId = null;
if (aMsg) {
envelopeId = aMsg.headers ? aMsg.headers["message-id"] : null;
}
let envelopeId =
aMsg && aMsg.headers && aMsg.headers["message-id"] || null;
sendTransactionCb(aDomMessage, errorCode, envelopeId);
});
});
@ -2181,7 +2215,8 @@ MmsService.prototype = {
aRequest.notifyGetMessageFailed(Ci.nsIMobileMessageCallback.INTERNAL_ERROR);
return;
}
if (DELIVERY_STATUS_PENDING == aMessageRecord.deliveryStatus) {
let deliveryStatus = aMessageRecord.deliveryInfo[0].deliveryStatus;
if (DELIVERY_STATUS_PENDING == deliveryStatus) {
if (DEBUG) debug("Delivery status of message record is 'pending'.");
aRequest.notifyGetMessageFailed(Ci.nsIMobileMessageCallback.INTERNAL_ERROR);
return;
@ -2375,6 +2410,9 @@ MmsService.prototype = {
case MMS.MMS_PDU_TYPE_DELIVERY_IND:
this.handleDeliveryIndication(msg);
break;
case MMS.MMS_PDU_TYPE_READ_ORIG_IND:
this.handleReadOriginateIndication(msg);
break;
default:
if (DEBUG) debug("Unsupported X-MMS-Message-Type: " + msg.type);
break;
@ -2395,12 +2433,3 @@ MmsService.prototype = {
};
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([MmsService]);
let debug;
if (DEBUG) {
debug = function (s) {
dump("-@- MmsService: " + s + "\n");
};
} else {
debug = function (s) {};
}

View File

@ -28,7 +28,7 @@ const DISABLE_MMS_GROUPING_FOR_RECEIVING = true;
const DB_NAME = "sms";
const DB_VERSION = 19;
const DB_VERSION = 20;
const MESSAGE_STORE_NAME = "sms";
const THREAD_STORE_NAME = "thread";
const PARTICIPANT_STORE_NAME = "participant";
@ -260,6 +260,10 @@ MobileMessageDatabaseService.prototype = {
self.upgradeSchema18(event.target.transaction, next);
break;
case 19:
if (DEBUG) debug("Upgrade to version 20. Add readStatus and readTimestamp.");
self.upgradeSchema19(event.target.transaction, next);
break;
case 20:
// This will need to be moved for each new version
if (DEBUG) debug("Upgrade finished.");
break;
@ -1181,6 +1185,63 @@ MobileMessageDatabaseService.prototype = {
};
},
/**
* Add readStatus and readTimestamp.
*/
upgradeSchema19: function upgradeSchema19(transaction, next) {
let messageStore = transaction.objectStore(MESSAGE_STORE_NAME);
messageStore.openCursor().onsuccess = function(event) {
let cursor = event.target.result;
if (!cursor) {
next();
return;
}
let messageRecord = cursor.value;
if (messageRecord.type == "sms") {
cursor.continue();
return;
}
// We can always retrieve transaction id from
// |messageRecord.headers["x-mms-transaction-id"]|.
if (messageRecord.hasOwnProperty("transactionId")) {
delete messageRecord.transactionId;
}
// xpconnect gives "undefined" for an unassigned argument of an interface
// method.
if (messageRecord.envelopeIdIndex === "undefined") {
delete messageRecord.envelopeIdIndex;
}
// Convert some header fields that were originally decoded as BooleanValue
// to numeric enums.
for (let field of ["x-mms-cancel-status",
"x-mms-sender-visibility",
"x-mms-read-status"]) {
let value = messageRecord.headers[field];
if (value !== undefined) {
messageRecord.headers[field] = value ? 128 : 129;
}
}
// For all sent and received MMS messages, we have to add their
// |readStatus| and |readTimestamp| attributes in |deliveryInfo| array.
let readReportRequested =
messageRecord.headers["x-mms-read-report"] || false;
for (let element of messageRecord.deliveryInfo) {
element.readStatus = readReportRequested
? MMS.DOM_READ_STATUS_PENDING
: MMS.DOM_READ_STATUS_NOT_APPLICABLE;
element.readTimestamp = 0;
}
cursor.update(messageRecord);
cursor.continue();
};
},
matchParsedPhoneNumbers: function matchParsedPhoneNumbers(addr1, parsedAddr1,
addr2, parsedAddr2) {
if ((parsedAddr1.internationalNumber &&
@ -1484,20 +1545,54 @@ MobileMessageDatabaseService.prototype = {
});
},
newTxnWithCallback: function newTxnWithCallback(aCallback, aFunc, aStoreNames) {
let self = this;
this.newTxn(READ_WRITE, function(aError, aTransaction, aStores) {
let notifyResult = function(aRv, aMessageRecord) {
if (!aCallback) {
return;
}
let domMessage =
aMessageRecord && self.createDomMessageFromRecord(aMessageRecord);
aCallback.notify(aRv, domMessage);
};
if (aError) {
// TODO bug 832140 check event.target.errorCode
notifyResult(Cr.NS_ERROR_FAILURE, null);
return;
}
let capture = {};
aTransaction.oncomplete = function(event) {
notifyResult(Cr.NS_OK, capture.messageRecord);
};
aTransaction.onabort = function(event) {
// TODO bug 832140 check event.target.errorCode
notifyResult(Cr.NS_ERROR_FAILURE, null);
};
aFunc(capture, aStores);
}, aStoreNames);
},
saveRecord: function saveRecord(aMessageRecord, aAddresses, aCallback) {
if (DEBUG) debug("Going to store " + JSON.stringify(aMessageRecord));
let self = this;
this.newTxn(READ_WRITE, function(error, txn, stores) {
let notifyResult = function(rv) {
if (aCallback) {
aCallback.notify(rv, self.createDomMessageFromRecord(aMessageRecord));
let notifyResult = function(aRv, aMessageRecord) {
if (!aCallback) {
return;
}
let domMessage =
aMessageRecord && self.createDomMessageFromRecord(aMessageRecord);
aCallback.notify(aRv, domMessage);
};
if (error) {
// TODO bug 832140 check event.target.errorCode
notifyResult(Cr.NS_ERROR_FAILURE);
notifyResult(Cr.NS_ERROR_FAILURE, null);
return;
}
@ -1505,11 +1600,11 @@ MobileMessageDatabaseService.prototype = {
if (aMessageRecord.id > self.lastMessageId) {
self.lastMessageId = aMessageRecord.id;
}
notifyResult(Cr.NS_OK);
notifyResult(Cr.NS_OK, aMessageRecord);
};
txn.onabort = function onabort(event) {
// TODO bug 832140 check event.target.errorCode
notifyResult(Cr.NS_ERROR_FAILURE);
notifyResult(Cr.NS_ERROR_FAILURE, null);
};
let messageStore = stores[0];
@ -1734,41 +1829,19 @@ MobileMessageDatabaseService.prototype = {
}
let self = this;
let messageRecord;
function notifyResult(rv) {
if (!callback) {
return;
}
let domMessage = self.createDomMessageFromRecord(messageRecord);
callback.notify(rv, domMessage);
}
this.newTxn(READ_WRITE, function (error, txn, messageStore) {
if (error) {
// TODO bug 832140 check event.target.errorCode
notifyResult(Cr.NS_ERROR_FAILURE);
return;
}
txn.oncomplete = function oncomplete(event) {
notifyResult(Cr.NS_OK);
};
txn.onabort = function onabort(event) {
// TODO bug 832140 check event.target.errorCode
notifyResult(Cr.NS_ERROR_FAILURE);
};
this.newTxnWithCallback(callback, function(aCapture, aMessageStore) {
let getRequest;
if (type === "messageId") {
getRequest = messageStore.get(id);
getRequest = aMessageStore.get(id);
} else if (type === "envelopeId") {
getRequest = messageStore.index("envelopeId").get(id);
getRequest = aMessageStore.index("envelopeId").get(id);
}
getRequest.onsuccess = function onsuccess(event) {
messageRecord = event.target.result;
let messageRecord = event.target.result;
if (!messageRecord) {
if (DEBUG) debug("type = " + id + " is not found");
return;
throw Cr.NS_ERROR_FAILURE;
}
let isRecordUpdated = false;
@ -1823,6 +1896,7 @@ MobileMessageDatabaseService.prototype = {
}
}
aCapture.messageRecord = messageRecord;
if (!isRecordUpdated) {
if (DEBUG) {
debug("The values of delivery, deliveryStatus and envelopeId " +
@ -1834,7 +1908,7 @@ MobileMessageDatabaseService.prototype = {
if (DEBUG) {
debug("The delivery, deliveryStatus or envelopeId are updated.");
}
messageStore.put(messageRecord);
aMessageStore.put(messageRecord);
};
});
},
@ -1940,21 +2014,30 @@ MobileMessageDatabaseService.prototype = {
saveReceivedMessage: function saveReceivedMessage(aMessage, aCallback) {
if ((aMessage.type != "sms" && aMessage.type != "mms") ||
(aMessage.type == "sms" && aMessage.messageClass == undefined) ||
(aMessage.type == "sms" && (aMessage.messageClass == undefined ||
aMessage.sender == undefined)) ||
(aMessage.type == "mms" && (aMessage.delivery == undefined ||
aMessage.transactionId == undefined ||
!Array.isArray(aMessage.deliveryInfo) ||
aMessage.deliveryStatus == undefined ||
!Array.isArray(aMessage.receivers))) ||
aMessage.sender == undefined ||
aMessage.timestamp == undefined) {
if (aCallback) {
aCallback.notify(Cr.NS_ERROR_FAILURE, null);
}
return;
}
let threadParticipants = [aMessage.sender];
let threadParticipants;
if (aMessage.type == "mms") {
if (aMessage.headers.from) {
aMessage.sender = aMessage.headers.from.address;
} else {
aMessage.sender = "anonymous";
}
threadParticipants = [aMessage.sender];
this.fillReceivedMmsThreadParticipants(aMessage, threadParticipants);
} else { // SMS
threadParticipants = [aMessage.sender];
}
let timestamp = aMessage.timestamp;
@ -1965,30 +2048,31 @@ MobileMessageDatabaseService.prototype = {
aMessage.read = FILTER_READ_UNREAD;
if (aMessage.type == "mms") {
aMessage.transactionIdIndex = aMessage.transactionId;
aMessage.transactionIdIndex = aMessage.headers["x-mms-transaction-id"];
aMessage.isReadReportSent = false;
// If |deliveryTimestamp| is not specified, use 0 as default.
let deliveryInfo = aMessage.deliveryInfo;
for (let i = 0; i < deliveryInfo.length; i++) {
if (deliveryInfo[i].deliveryTimestamp == undefined) {
deliveryInfo[i].deliveryTimestamp = 0;
}
}
// As a receiver, we don't need to care about the delivery status of
// others, so we put a single element with self's phone number in the
// |deliveryInfo| array.
aMessage.deliveryInfo = [{
receiver: aMessage.phoneNumber,
deliveryStatus: aMessage.deliveryStatus,
deliveryTimestamp: 0,
readStatus: MMS.DOM_READ_STATUS_NOT_APPLICABLE,
readTimestamp: 0,
}];
delete aMessage.deliveryStatus;
}
if (aMessage.type == "sms") {
aMessage.delivery = DELIVERY_RECEIVED;
aMessage.deliveryStatus = DELIVERY_STATUS_SUCCESS;
aMessage.deliveryTimestamp = 0;
if (aMessage.pid == undefined) {
aMessage.pid = RIL.PDU_PID_DEFAULT;
}
// If |deliveryTimestamp| is not specified, use 0 as default.
if (aMessage.deliveryTimestamp == undefined) {
aMessage.deliveryTimestamp = 0;
}
}
aMessage.deliveryIndex = [aMessage.delivery, timestamp];
@ -2029,12 +2113,18 @@ MobileMessageDatabaseService.prototype = {
}
return;
}
let readStatus = aMessage.headers["x-mms-read-report"]
? MMS.DOM_READ_STATUS_PENDING
: MMS.DOM_READ_STATUS_NOT_APPLICABLE;
aMessage.deliveryInfo = [];
for (let i = 0; i < receivers.length; i++) {
aMessage.deliveryInfo.push({
receiver: receivers[i],
deliveryStatus: deliveryStatus,
deliveryTimestamp: 0 });
deliveryTimestamp: 0,
readStatus: readStatus,
readTimestamp: 0,
});
}
}
@ -2065,12 +2155,62 @@ MobileMessageDatabaseService.prototype = {
},
setMessageDeliveryByEnvelopeId: function setMessageDeliveryByEnvelopeId(
envelopeId, receiver, delivery, deliveryStatus, callback) {
this.updateMessageDeliveryById(envelopeId, "envelopeId",
receiver, delivery, deliveryStatus,
null, callback);
setMessageDeliveryStatusByEnvelopeId:
function setMessageDeliveryStatusByEnvelopeId(aEnvelopeId, aReceiver,
aDeliveryStatus, aCallback) {
this.updateMessageDeliveryById(aEnvelopeId, "envelopeId", aReceiver, null,
aDeliveryStatus, null, aCallback);
},
setMessageReadStatusByEnvelopeId:
function setMessageReadStatusByEnvelopeId(aEnvelopeId, aReceiver,
aReadStatus, aCallback) {
if (DEBUG) {
debug("Setting message's read status by envelopeId = " + aEnvelopeId +
", receiver: " + aReceiver + ", readStatus: " + aReadStatus);
}
let self = this;
this.newTxnWithCallback(aCallback, function(aCapture, aMessageStore) {
let getRequest = aMessageStore.index("envelopeId").get(aEnvelopeId);
getRequest.onsuccess = function onsuccess(event) {
let messageRecord = event.target.result;
if (!messageRecord) {
if (DEBUG) debug("envelopeId '" + aEnvelopeId + "' not found");
throw Cr.NS_ERROR_FAILURE;
}
aCapture.messageRecord = messageRecord;
let isRecordUpdated = false;
self.forEachMatchedMmsDeliveryInfo(messageRecord.deliveryInfo,
aReceiver, function(aEntry) {
if (aEntry.readStatus == aReadStatus) {
return;
}
aEntry.readStatus = aReadStatus;
if (aReadStatus == MMS.DOM_READ_STATUS_SUCCESS) {
aEntry.readTimestamp = Date.now();
} else {
aEntry.readTimestamp = 0;
}
isRecordUpdated = true;
});
if (!isRecordUpdated) {
if (DEBUG) {
debug("The values of readStatus don't need to be updated.");
}
return;
}
if (DEBUG) {
debug("The readStatus is updated.");
}
aMessageStore.put(messageRecord);
};
});
},
getMessageRecordByTransactionId: function getMessageRecordByTransactionId(aTransactionId, aCallback) {

View File

@ -95,6 +95,21 @@ this.MMS_PDU_STATUS_INDETERMINATE = 133;
this.MMS_PDU_STATUS_FORWARDED = 134;
this.MMS_PDU_STATUS_UNREACHABLE = 135;
// X-Mms-Cancel-Status values
// @see OMA-TS-MMS_ENC-V1_3-20110913-A clause 7.3.7
this.MMS_PDU_CANCEL_STATUS_RECEIVED = 128;
this.MMS_PDU_CANCEL_STATUS_CORRUPTED = 129;
// X-Mms-Sender-Visibility
// @see OMA-TS-MMS_ENC-V1_3-20110913-A clause 7.3.52
this.MMS_PDU_SENDER_VISIBILITY_HIDE = 128;
this.MMS_PDU_SENDER_VISIBILITY_SHOW = 129;
// X-Mms-Read-Status
// @see OMA-TS-MMS_ENC-V1_3-20110913-A clause 7.3.38
this.MMS_PDU_READ_STATUS_READ = 128;
this.MMS_PDU_READ_STATUS_DELETED_UNREAD = 129;
// Maximum Values of MMS Parameters
// @see OMA-TS-MMS_CONF-V1_3-20110511-C 10.2.5
this.MMS_MAX_LENGTH_SUBJECT = 40;
@ -103,6 +118,11 @@ this.MMS_MAX_TOTAL_RECIPIENTS = 20;
this.MMS_MAX_LENGTH_NAME_CONTENT_TYPE = 40;
this.MMS_MAX_LENGTH_MAILBOX_PORTION = 256;
this.DOM_READ_STATUS_NOT_APPLICABLE = "not-applicable";
this.DOM_READ_STATUS_SUCCESS = "success";
this.DOM_READ_STATUS_PENDING = "pending";
this.DOM_READ_STATUS_ERROR = "error";
this.ALL_CONST_SYMBOLS = undefined; // We want ALL_CONST_SYMBOLS to be exported.
this.ALL_CONST_SYMBOLS = Object.keys(this);

View File

@ -118,6 +118,10 @@ child:
NotifyReceivedSilentMessage(MobileMessageData aMessageData);
NotifyReadSuccessMessage(MobileMessageData aMessageData);
NotifyReadErrorMessage(MobileMessageData aMessageData);
parent:
/**
* Sent when the child no longer needs to use sms.

View File

@ -118,6 +118,20 @@ SmsChild::RecvNotifyReceivedSilentMessage(const MobileMessageData& aData)
return true;
}
bool
SmsChild::RecvNotifyReadSuccessMessage(const MobileMessageData& aData)
{
NotifyObserversWithMobileMessage(kSmsReadSuccessObserverTopic, aData);
return true;
}
bool
SmsChild::RecvNotifyReadErrorMessage(const MobileMessageData& aData)
{
NotifyObserversWithMobileMessage(kSmsReadErrorObserverTopic, aData);
return true;
}
PSmsRequestChild*
SmsChild::AllocPSmsRequestChild(const IPCSmsRequest& aRequest)
{

View File

@ -58,6 +58,12 @@ protected:
virtual bool
RecvNotifyReceivedSilentMessage(const MobileMessageData& aMessage) MOZ_OVERRIDE;
virtual bool
RecvNotifyReadSuccessMessage(const MobileMessageData& aMessage) MOZ_OVERRIDE;
virtual bool
RecvNotifyReadErrorMessage(const MobileMessageData& aMessage) MOZ_OVERRIDE;
virtual PSmsRequestChild*
AllocPSmsRequestChild(const IPCSmsRequest& aRequest) MOZ_OVERRIDE;

View File

@ -153,6 +153,8 @@ SmsParent::SmsParent()
obs->AddObserver(this, kSmsDeliverySuccessObserverTopic, false);
obs->AddObserver(this, kSmsDeliveryErrorObserverTopic, false);
obs->AddObserver(this, kSilentSmsReceivedObserverTopic, false);
obs->AddObserver(this, kSmsReadSuccessObserverTopic, false);
obs->AddObserver(this, kSmsReadErrorObserverTopic, false);
}
void
@ -171,6 +173,8 @@ SmsParent::ActorDestroy(ActorDestroyReason why)
obs->RemoveObserver(this, kSmsDeliverySuccessObserverTopic);
obs->RemoveObserver(this, kSmsDeliveryErrorObserverTopic);
obs->RemoveObserver(this, kSilentSmsReceivedObserverTopic);
obs->RemoveObserver(this, kSmsReadSuccessObserverTopic);
obs->RemoveObserver(this, kSmsReadErrorObserverTopic);
}
NS_IMETHODIMP
@ -272,6 +276,30 @@ SmsParent::Observe(nsISupports* aSubject, const char* aTopic,
return NS_OK;
}
if (!strcmp(aTopic, kSmsReadSuccessObserverTopic)) {
MobileMessageData msgData;
if (!GetMobileMessageDataFromMessage(aSubject, msgData)) {
NS_ERROR("Got a 'sms-read-success' topic without a valid message!");
return NS_OK;
}
unused << SendNotifyReadSuccessMessage(msgData);
return NS_OK;
}
if (!strcmp(aTopic, kSmsReadErrorObserverTopic)) {
MobileMessageData msgData;
if (!GetMobileMessageDataFromMessage(aSubject, msgData)) {
NS_ERROR("Got a 'sms-read-error' topic without a valid message!");
return NS_OK;
}
unused << SendNotifyReadErrorMessage(msgData);
return NS_OK;
}
return NS_OK;
}

View File

@ -9,6 +9,7 @@ include protocol PBlob;
using DeliveryState from "mozilla/dom/mobilemessage/Types.h";
using DeliveryStatus from "mozilla/dom/mobilemessage/Types.h";
using MessageClass from "mozilla/dom/mobilemessage/Types.h";
using ReadStatus from "mozilla/dom/mobilemessage/Types.h";
using ReadState from "mozilla/dom/mobilemessage/Types.h";
using MessageType from "mozilla/dom/mobilemessage/Types.h";
@ -51,6 +52,8 @@ struct MmsDeliveryInfoData
nsString receiver;
DeliveryStatus deliveryStatus;
uint64_t deliveryTimestamp;
ReadStatus readStatus;
uint64_t readTimestamp;
};
struct MmsMessageData

View File

@ -177,6 +177,38 @@ add_test(function test_MmsHeader_encode() {
run_next_test();
});
//
// Test target: CancelStatusValue
//
//// CancelStatusValue.decode ////
add_test(function test_CancelStatusValue_decode() {
for (let i = 0; i < 256; i++) {
if ((i >= 128) && (i <= 129)) {
wsp_decode_test(MMS.CancelStatusValue, [i], i);
} else {
wsp_decode_test(MMS.CancelStatusValue, [i], null, "CodeError");
}
}
run_next_test();
});
//// CancelStatusValue.encode ////
add_test(function test_CancelStatusValue_encode() {
for (let i = 0; i < 256; i++) {
if ((i >= 128) && (i <= 129)) {
wsp_encode_test(MMS.CancelStatusValue, i, [i]);
} else {
wsp_encode_test(MMS.CancelStatusValue, i, null, "CodeError");
}
}
run_next_test();
});
//
// Test target: ContentClassValue
//
@ -657,6 +689,38 @@ add_test(function test_PriorityValue_encode() {
run_next_test();
});
//
// Test target: ReadStatusValue
//
//// ReadStatusValue.decode ////
add_test(function test_ReadStatusValue_decode() {
for (let i = 0; i < 256; i++) {
if ((i >= 128) && (i <= 129)) {
wsp_decode_test(MMS.ReadStatusValue, [i], i);
} else {
wsp_decode_test(MMS.ReadStatusValue, [i], null, "CodeError");
}
}
run_next_test();
});
//// ReadStatusValue.encode ////
add_test(function test_ReadStatusValue_encode() {
for (let i = 0; i < 256; i++) {
if ((i >= 128) && (i <= 129)) {
wsp_encode_test(MMS.ReadStatusValue, i, [i]);
} else {
wsp_encode_test(MMS.ReadStatusValue, i, null, "CodeError");
}
}
run_next_test();
});
//
// Test target: RecommendedRetrievalModeValue
//
@ -765,6 +829,38 @@ add_test(function test_RetrieveStatusValue_decode() {
run_next_test();
});
//
// Test target: SenderVisibilityValue
//
//// SenderVisibilityValue.decode ////
add_test(function test_SenderVisibilityValue_decode() {
for (let i = 0; i < 256; i++) {
if ((i >= 128) && (i <= 129)) {
wsp_decode_test(MMS.SenderVisibilityValue, [i], i);
} else {
wsp_decode_test(MMS.SenderVisibilityValue, [i], null, "CodeError");
}
}
run_next_test();
});
//// SenderVisibilityValue.encode ////
add_test(function test_SenderVisibilityValue_encode() {
for (let i = 0; i < 256; i++) {
if ((i >= 128) && (i <= 129)) {
wsp_encode_test(MMS.SenderVisibilityValue, i, [i]);
} else {
wsp_encode_test(MMS.SenderVisibilityValue, i, null, "CodeError");
}
}
run_next_test();
});
//
// Test target: StatusValue
//

View File

@ -198,15 +198,15 @@ var WifiManager = (function() {
// can trigger bugs in some drivers.
// On properly written drivers, bringing the interface
// down powers down the interface.
callback(0);
notify("supplicantlost", { success: true });
callback(0);
return;
}
wifiCommand.unloadDriver(function(status) {
driverLoaded = (status < 0);
callback(status);
notify("supplicantlost", { success: true });
callback(status);
});
}
@ -419,12 +419,6 @@ var WifiManager = (function() {
}
function notifyStateChange(fields) {
// Don't handle any state change when and after disabling.
if (manager.state === "DISABLING" ||
manager.state === "UNINITIALIZED") {
return false;
}
// If we're already in the COMPLETED state, we might receive events from
// the supplicant that tell us that we're re-authenticating or reminding
// us that we're associated to a network. In those cases, we don't need to
@ -446,12 +440,19 @@ var WifiManager = (function() {
fields.state === "COMPLETED")) {
setBackgroundScan("OFF", function() {});
}
fields.prevState = manager.state;
manager.state = fields.state;
fields.prevState = manager.state;
// Detect wpa_supplicant's loop iterations.
manager.supplicantLoopDetection(fields.prevState, fields.state);
notify("statechange", fields);
// Don't update state when and after disabling.
if (manager.state === "DISABLING" ||
manager.state === "UNINITIALIZED") {
return false;
}
manager.state = fields.state;
return true;
}

View File

@ -99,7 +99,9 @@ class MochitestRunner(MozbuildObject):
self.mochitest_dir = os.path.join(self.tests_dir, 'testing', 'mochitest')
self.lib_dir = os.path.join(self.topobjdir, 'dist', 'lib')
def run_b2g_test(self, test_file=None, b2g_home=None, xre_path=None, **kwargs):
def run_b2g_test(self, test_file=None, b2g_home=None, xre_path=None,
total_chunks=None, this_chunk=None, no_window=None,
**kwargs):
"""Runs a b2g mochitest.
test_file is a path to a test file. It can be a relative path from the
@ -144,6 +146,9 @@ class MochitestRunner(MozbuildObject):
for k, v in kwargs.iteritems():
setattr(options, k, v)
options.noWindow = no_window
options.totalChunks = total_chunks
options.thisChunk = this_chunk
options.consoleLevel = 'INFO'
if conditions.is_b2g_desktop(self):