tenfourfox/dom/mobilemessage/MmsMessageInternal.cpp

615 lines
18 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "MmsMessageInternal.h"
#include "nsIDOMClassInfo.h"
#include "jsapi.h" // For JS_IsArrayObject, JS_GetElement, etc.
#include "nsJSUtils.h"
#include "nsContentUtils.h"
#include "nsTArrayHelpers.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/File.h"
#include "mozilla/dom/mobilemessage/Constants.h" // For MessageType
#include "mozilla/dom/mobilemessage/SmsTypes.h"
#include "mozilla/dom/ScriptSettings.h"
#include "mozilla/dom/ToJSValue.h"
#include "mozilla/dom/ipc/BlobChild.h"
#include "mozilla/dom/ipc/BlobParent.h"
namespace mozilla {
namespace dom {
namespace mobilemessage {
NS_IMPL_CYCLE_COLLECTION_CLASS(MmsMessageInternal)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(MmsMessageInternal)
for (uint32_t i = 0; i < tmp->mAttachments.Length(); i++) {
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAttachments[i].mContent)
}
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(MmsMessageInternal)
for (uint32_t i = 0; i < tmp->mAttachments.Length(); i++) {
NS_IMPL_CYCLE_COLLECTION_UNLINK(mAttachments[i].mContent)
}
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MmsMessageInternal)
NS_INTERFACE_MAP_ENTRY(nsIMmsMessage)
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(MmsMessageInternal)
NS_IMPL_CYCLE_COLLECTING_RELEASE(MmsMessageInternal)
MmsMessageInternal::MmsMessageInternal(int32_t aId,
uint64_t aThreadId,
const nsAString& aIccId,
DeliveryState aDelivery,
const nsTArray<MmsDeliveryInfo>& aDeliveryInfo,
const nsAString& aSender,
const nsTArray<nsString>& aReceivers,
uint64_t aTimestamp,
uint64_t aSentTimestamp,
bool aRead,
const nsAString& aSubject,
const nsAString& aSmil,
const nsTArray<MmsAttachment>& aAttachments,
uint64_t aExpiryDate,
bool aReadReportRequested)
: mId(aId),
mThreadId(aThreadId),
mIccId(aIccId),
mDelivery(aDelivery),
mDeliveryInfo(aDeliveryInfo),
mSender(aSender),
mReceivers(aReceivers),
mTimestamp(aTimestamp),
mSentTimestamp(aSentTimestamp),
mRead(aRead),
mSubject(aSubject),
mSmil(aSmil),
mAttachments(aAttachments),
mExpiryDate(aExpiryDate),
mReadReportRequested(aReadReportRequested)
{
}
MmsMessageInternal::MmsMessageInternal(const MmsMessageData& aData)
: mId(aData.id())
, mThreadId(aData.threadId())
, mIccId(aData.iccId())
, mDelivery(aData.delivery())
, mSender(aData.sender())
, mReceivers(aData.receivers())
, mTimestamp(aData.timestamp())
, mSentTimestamp(aData.sentTimestamp())
, mRead(aData.read())
, mSubject(aData.subject())
, mSmil(aData.smil())
, mExpiryDate(aData.expiryDate())
, mReadReportRequested(aData.readReportRequested())
{
uint32_t len = aData.attachments().Length();
mAttachments.SetCapacity(len);
for (uint32_t i = 0; i < len; i++) {
MmsAttachment att;
const MmsAttachmentData &element = aData.attachments()[i];
att.mId = element.id();
att.mLocation = element.location();
// mContent is not going to be exposed to JS directly so we can use
// nullptr as parent.
if (element.contentParent()) {
RefPtr<BlobImpl> impl = static_cast<BlobParent*>(element.contentParent())->GetBlobImpl();
att.mContent = Blob::Create(nullptr, impl);
} else if (element.contentChild()) {
RefPtr<BlobImpl> impl = static_cast<BlobChild*>(element.contentChild())->GetBlobImpl();
att.mContent = Blob::Create(nullptr, impl);
} else {
NS_WARNING("MmsMessage: Unable to get attachment content.");
}
mAttachments.AppendElement(att);
}
len = aData.deliveryInfo().Length();
mDeliveryInfo.SetCapacity(len);
for (uint32_t i = 0; i < len; i++) {
MmsDeliveryInfo info;
const MmsDeliveryInfoData &infoData = aData.deliveryInfo()[i];
// Prepare |info.mReceiver|.
info.mReceiver = infoData.receiver();
// Prepare |info.mDeliveryStatus|.
nsString statusStr;
switch (infoData.deliveryStatus()) {
case eDeliveryStatus_NotApplicable:
statusStr = DELIVERY_STATUS_NOT_APPLICABLE;
break;
case eDeliveryStatus_Success:
statusStr = DELIVERY_STATUS_SUCCESS;
break;
case eDeliveryStatus_Pending:
statusStr = DELIVERY_STATUS_PENDING;
break;
case eDeliveryStatus_Error:
statusStr = DELIVERY_STATUS_ERROR;
break;
case eDeliveryStatus_Reject:
statusStr = DELIVERY_STATUS_REJECTED;
break;
case eDeliveryStatus_Manual:
statusStr = DELIVERY_STATUS_MANUAL;
break;
case eDeliveryStatus_EndGuard:
default:
MOZ_CRASH("We shouldn't get any other delivery status!");
}
info.mDeliveryStatus = statusStr;
// Prepare |info.mDeliveryTimestamp|.
info.mDeliveryTimestamp = infoData.deliveryTimestamp();
// Prepare |info.mReadStatus|.
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.mReadStatus = statusReadString;
// Prepare |info.mReadTimestamp|.
info.mReadTimestamp = infoData.readTimestamp();
mDeliveryInfo.AppendElement(info);
}
}
/* static */ nsresult
MmsMessageInternal::Create(int32_t aId,
uint64_t aThreadId,
const nsAString& aIccId,
const nsAString& aDelivery,
const JS::Value& aDeliveryInfo,
const nsAString& aSender,
const JS::Value& aReceivers,
uint64_t aTimestamp,
uint64_t aSentTimestamp,
bool aRead,
const nsAString& aSubject,
const nsAString& aSmil,
const JS::Value& aAttachments,
uint64_t aExpiryDate,
bool aIsReadReportRequested,
JSContext* aCx,
nsIMmsMessage** aMessage)
{
*aMessage = nullptr;
// Set |delivery|.
DeliveryState delivery;
if (aDelivery.Equals(DELIVERY_SENT)) {
delivery = eDeliveryState_Sent;
} else if (aDelivery.Equals(DELIVERY_RECEIVED)) {
delivery = eDeliveryState_Received;
} else if (aDelivery.Equals(DELIVERY_SENDING)) {
delivery = eDeliveryState_Sending;
} else if (aDelivery.Equals(DELIVERY_NOT_DOWNLOADED)) {
delivery = eDeliveryState_NotDownloaded;
} else if (aDelivery.Equals(DELIVERY_ERROR)) {
delivery = eDeliveryState_Error;
} else {
return NS_ERROR_INVALID_ARG;
}
// Set |deliveryInfo|.
if (!aDeliveryInfo.isObject()) {
return NS_ERROR_INVALID_ARG;
}
JS::Rooted<JSObject*> deliveryInfoObj(aCx, &aDeliveryInfo.toObject());
bool isArray;
if (!JS_IsArrayObject(aCx, deliveryInfoObj, &isArray)) {
return NS_ERROR_FAILURE;
}
if (!isArray) {
return NS_ERROR_INVALID_ARG;
}
uint32_t length;
MOZ_ALWAYS_TRUE(JS_GetArrayLength(aCx, deliveryInfoObj, &length));
nsTArray<MmsDeliveryInfo> deliveryInfo;
JS::Rooted<JS::Value> infoJsVal(aCx);
for (uint32_t i = 0; i < length; ++i) {
if (!JS_GetElement(aCx, deliveryInfoObj, i, &infoJsVal) ||
!infoJsVal.isObject()) {
return NS_ERROR_INVALID_ARG;
}
MmsDeliveryInfo info;
if (!info.Init(aCx, infoJsVal)) {
return NS_ERROR_TYPE_ERR;
}
deliveryInfo.AppendElement(info);
}
// Set |receivers|.
if (!aReceivers.isObject()) {
return NS_ERROR_INVALID_ARG;
}
JS::Rooted<JSObject*> receiversObj(aCx, &aReceivers.toObject());
if (!JS_IsArrayObject(aCx, receiversObj, &isArray)) {
return NS_ERROR_FAILURE;
}
if (!isArray) {
return NS_ERROR_INVALID_ARG;
}
MOZ_ALWAYS_TRUE(JS_GetArrayLength(aCx, receiversObj, &length));
nsTArray<nsString> receivers;
JS::Rooted<JS::Value> receiverJsVal(aCx);
for (uint32_t i = 0; i < length; ++i) {
if (!JS_GetElement(aCx, receiversObj, i, &receiverJsVal) ||
!receiverJsVal.isString()) {
return NS_ERROR_INVALID_ARG;
}
nsAutoJSString receiverStr;
if (!receiverStr.init(aCx, receiverJsVal.toString())) {
return NS_ERROR_FAILURE;
}
receivers.AppendElement(receiverStr);
}
// Set |attachments|.
if (!aAttachments.isObject()) {
return NS_ERROR_INVALID_ARG;
}
JS::Rooted<JSObject*> attachmentsObj(aCx, &aAttachments.toObject());
if (!JS_IsArrayObject(aCx, attachmentsObj, &isArray)) {
return NS_ERROR_FAILURE;
}
if (!isArray) {
return NS_ERROR_INVALID_ARG;
}
nsTArray<MmsAttachment> attachments;
MOZ_ALWAYS_TRUE(JS_GetArrayLength(aCx, attachmentsObj, &length));
JS::Rooted<JS::Value> attachmentJsVal(aCx);
for (uint32_t i = 0; i < length; ++i) {
if (!JS_GetElement(aCx, attachmentsObj, i, &attachmentJsVal)) {
return NS_ERROR_INVALID_ARG;
}
MmsAttachment attachment;
if (!attachment.Init(aCx, attachmentJsVal)) {
return NS_ERROR_TYPE_ERR;
}
NS_ENSURE_TRUE(attachment.mContent, NS_ERROR_TYPE_ERR);
attachments.AppendElement(attachment);
}
nsCOMPtr<nsIMmsMessage> message =
new MmsMessageInternal(aId,
aThreadId,
aIccId,
delivery,
deliveryInfo,
aSender,
receivers,
aTimestamp,
aSentTimestamp,
aRead,
aSubject,
aSmil,
attachments,
aExpiryDate,
aIsReadReportRequested);
message.forget(aMessage);
return NS_OK;
}
bool
MmsMessageInternal::GetData(ContentParent* aParent,
MmsMessageData& aData)
{
NS_ASSERTION(aParent, "aParent is null");
aData.id() = mId;
aData.threadId() = mThreadId;
aData.iccId() = mIccId;
aData.delivery() = mDelivery;
aData.sender().Assign(mSender);
aData.receivers() = mReceivers;
aData.timestamp() = mTimestamp;
aData.sentTimestamp() = mSentTimestamp;
aData.read() = mRead;
aData.subject() = mSubject;
aData.smil() = mSmil;
aData.expiryDate() = mExpiryDate;
aData.readReportRequested() = mReadReportRequested;
aData.deliveryInfo().SetCapacity(mDeliveryInfo.Length());
for (uint32_t i = 0; i < mDeliveryInfo.Length(); i++) {
MmsDeliveryInfoData infoData;
const MmsDeliveryInfo &info = mDeliveryInfo[i];
// Prepare |infoData.mReceiver|.
infoData.receiver().Assign(info.mReceiver);
// Prepare |infoData.mDeliveryStatus|.
DeliveryStatus status;
if (info.mDeliveryStatus.Equals(DELIVERY_STATUS_NOT_APPLICABLE)) {
status = eDeliveryStatus_NotApplicable;
} else if (info.mDeliveryStatus.Equals(DELIVERY_STATUS_SUCCESS)) {
status = eDeliveryStatus_Success;
} else if (info.mDeliveryStatus.Equals(DELIVERY_STATUS_PENDING)) {
status = eDeliveryStatus_Pending;
} else if (info.mDeliveryStatus.Equals(DELIVERY_STATUS_ERROR)) {
status = eDeliveryStatus_Error;
} else if (info.mDeliveryStatus.Equals(DELIVERY_STATUS_REJECTED)) {
status = eDeliveryStatus_Reject;
} else if (info.mDeliveryStatus.Equals(DELIVERY_STATUS_MANUAL)) {
status = eDeliveryStatus_Manual;
} else {
return false;
}
infoData.deliveryStatus() = status;
// Prepare |infoData.mDeliveryTimestamp|.
infoData.deliveryTimestamp() = info.mDeliveryTimestamp;
// Prepare |infoData.mReadStatus|.
ReadStatus readStatus;
if (info.mReadStatus.Equals(READ_STATUS_NOT_APPLICABLE)) {
readStatus = eReadStatus_NotApplicable;
} else if (info.mReadStatus.Equals(READ_STATUS_SUCCESS)) {
readStatus = eReadStatus_Success;
} else if (info.mReadStatus.Equals(READ_STATUS_PENDING)) {
readStatus = eReadStatus_Pending;
} else if (info.mReadStatus.Equals(READ_STATUS_ERROR)) {
readStatus = eReadStatus_Error;
} else {
return false;
}
infoData.readStatus() = readStatus;
// Prepare |infoData.mReadTimestamp|.
infoData.readTimestamp() = info.mReadTimestamp;
aData.deliveryInfo().AppendElement(infoData);
}
aData.attachments().SetCapacity(mAttachments.Length());
for (uint32_t i = 0; i < mAttachments.Length(); i++) {
MmsAttachmentData mma;
const MmsAttachment &element = mAttachments[i];
mma.id().Assign(element.mId);
mma.location().Assign(element.mLocation);
// This is a workaround. Sometimes the blob we get from the database
// doesn't have a valid last modified date, making the ContentParent
// send a "Mystery Blob" to the ContentChild. Attempting to get the
// last modified date of blob can force that value to be initialized.
RefPtr<BlobImpl> impl = element.mContent->Impl();
if (impl && impl->IsDateUnknown()) {
ErrorResult rv;
impl->GetLastModified(rv);
if (rv.Failed()) {
NS_WARNING("Failed to get last modified date!");
rv.SuppressException();
}
}
mma.contentParent() = aParent->GetOrCreateActorForBlob(element.mContent);
if (!mma.contentParent()) {
return false;
}
aData.attachments().AppendElement(mma);
}
return true;
}
NS_IMETHODIMP
MmsMessageInternal::GetType(nsAString& aType)
{
aType = NS_LITERAL_STRING("mms");
return NS_OK;
}
NS_IMETHODIMP
MmsMessageInternal::GetId(int32_t* aId)
{
*aId = mId;
return NS_OK;
}
NS_IMETHODIMP
MmsMessageInternal::GetThreadId(uint64_t* aThreadId)
{
*aThreadId = mThreadId;
return NS_OK;
}
NS_IMETHODIMP
MmsMessageInternal::GetIccId(nsAString& aIccId)
{
aIccId = mIccId;
return NS_OK;
}
NS_IMETHODIMP
MmsMessageInternal::GetDelivery(nsAString& aDelivery)
{
switch (mDelivery) {
case eDeliveryState_Received:
aDelivery = DELIVERY_RECEIVED;
break;
case eDeliveryState_Sending:
aDelivery = DELIVERY_SENDING;
break;
case eDeliveryState_Sent:
aDelivery = DELIVERY_SENT;
break;
case eDeliveryState_Error:
aDelivery = DELIVERY_ERROR;
break;
case eDeliveryState_NotDownloaded:
aDelivery = DELIVERY_NOT_DOWNLOADED;
break;
case eDeliveryState_Unknown:
case eDeliveryState_EndGuard:
default:
MOZ_CRASH("We shouldn't get any other delivery state!");
}
return NS_OK;
}
NS_IMETHODIMP
MmsMessageInternal::GetDeliveryInfo(JSContext* aCx, JS::MutableHandle<JS::Value> aDeliveryInfo)
{
// TODO Bug 850525 It'd be better to depend on the delivery of MmsMessage
// to return a more correct value. Ex, if .delivery = 'received', we should
// also make .deliveryInfo = null, since the .deliveryInfo is useless.
uint32_t length = mDeliveryInfo.Length();
if (length == 0) {
aDeliveryInfo.setNull();
return NS_OK;
}
if (!ToJSValue(aCx, mDeliveryInfo, aDeliveryInfo)) {
return NS_ERROR_OUT_OF_MEMORY;
}
return NS_OK;
}
NS_IMETHODIMP
MmsMessageInternal::GetSender(nsAString& aSender)
{
aSender = mSender;
return NS_OK;
}
NS_IMETHODIMP
MmsMessageInternal::GetReceivers(JSContext* aCx, JS::MutableHandle<JS::Value> aReceivers)
{
JS::Rooted<JSObject*> receiversObj(aCx);
nsresult rv = nsTArrayToJSArray(aCx, mReceivers, &receiversObj);
NS_ENSURE_SUCCESS(rv, rv);
aReceivers.setObject(*receiversObj);
return NS_OK;
}
NS_IMETHODIMP
MmsMessageInternal::GetTimestamp(DOMTimeStamp* aTimestamp)
{
*aTimestamp = mTimestamp;
return NS_OK;
}
NS_IMETHODIMP
MmsMessageInternal::GetSentTimestamp(DOMTimeStamp* aSentTimestamp)
{
*aSentTimestamp = mSentTimestamp;
return NS_OK;
}
NS_IMETHODIMP
MmsMessageInternal::GetRead(bool* aRead)
{
*aRead = mRead;
return NS_OK;
}
NS_IMETHODIMP
MmsMessageInternal::GetSubject(nsAString& aSubject)
{
aSubject = mSubject;
return NS_OK;
}
NS_IMETHODIMP
MmsMessageInternal::GetSmil(nsAString& aSmil)
{
aSmil = mSmil;
return NS_OK;
}
NS_IMETHODIMP
MmsMessageInternal::GetAttachments(JSContext* aCx, JS::MutableHandle<JS::Value> aAttachments)
{
uint32_t length = mAttachments.Length();
if (length == 0) {
aAttachments.setNull();
return NS_OK;
}
// Duplicating the Blob with the correct parent object.
nsIGlobalObject* global = xpc::NativeGlobal(JS::CurrentGlobalOrNull(aCx));
MOZ_ASSERT(global);
nsTArray<MmsAttachment> result;
for (uint32_t i = 0; i < length; i++) {
MmsAttachment attachment;
const MmsAttachment &element = mAttachments[i];
attachment.mId = element.mId;
attachment.mLocation = element.mLocation;
attachment.mContent = Blob::Create(global, element.mContent->Impl());
result.AppendElement(attachment);
}
if (!ToJSValue(aCx, result, aAttachments)) {
JS_ClearPendingException(aCx);
return NS_ERROR_TYPE_ERR;
}
return NS_OK;
}
NS_IMETHODIMP
MmsMessageInternal::GetExpiryDate(DOMTimeStamp* aExpiryDate)
{
*aExpiryDate = mExpiryDate;
return NS_OK;
}
NS_IMETHODIMP
MmsMessageInternal::GetReadReportRequested(bool* aReadReportRequested)
{
*aReadReportRequested = mReadReportRequested;
return NS_OK;
}
} // namespace mobilemessage
} // namespace dom
} // namespace mozilla