713 lines
24 KiB
C++
713 lines
24 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 "mozilla/dom/MLS.h"
|
|
#include "mozilla/dom/MLSGroupView.h"
|
|
#include "mozilla/dom/TypedArray.h"
|
|
#include "mozilla/dom/Promise.h"
|
|
#include "nsTArray.h"
|
|
#include "nsCOMPtr.h"
|
|
#include "nsIGlobalObject.h"
|
|
#include "mozilla/ipc/PBackgroundChild.h"
|
|
#include "mozilla/ipc/BackgroundChild.h"
|
|
#include "mozilla/dom/MLSTransactionChild.h"
|
|
#include "mozilla/dom/MLSTransactionMessage.h"
|
|
#include "mozilla/dom/PMLSTransaction.h"
|
|
#include "mozilla/ipc/Endpoint.h"
|
|
#include "mozilla/BasePrincipal.h"
|
|
#include "MLSGroupView.h"
|
|
#include "nsTArray.h"
|
|
#include "mozilla/Logging.h"
|
|
#include "mozilla/Span.h"
|
|
#include "nsDebug.h"
|
|
#include "MLSLogging.h"
|
|
#include "MLSTypeUtils.h"
|
|
|
|
namespace mozilla::dom {
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(MLS, mGlobalObject)
|
|
|
|
NS_IMPL_CYCLE_COLLECTING_ADDREF(MLS)
|
|
NS_IMPL_CYCLE_COLLECTING_RELEASE(MLS)
|
|
|
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MLS)
|
|
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
|
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
|
NS_INTERFACE_MAP_END
|
|
|
|
// Setup logging
|
|
mozilla::LazyLogModule gMlsLog("MLS");
|
|
|
|
/* static */ already_AddRefed<MLS> MLS::Constructor(GlobalObject& aGlobalObject,
|
|
ErrorResult& aRv) {
|
|
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug, ("MLS::Constructor()"));
|
|
|
|
nsCOMPtr<nsIGlobalObject> global(
|
|
do_QueryInterface(aGlobalObject.GetAsSupports()));
|
|
if (NS_WARN_IF(!global)) {
|
|
aRv.Throw(NS_ERROR_FAILURE);
|
|
return nullptr;
|
|
}
|
|
|
|
// Get the principal and perform some validation on it.
|
|
// We do not allow MLS in Private Browsing Mode for now.
|
|
nsIPrincipal* principal = global->PrincipalOrNull();
|
|
if (!principal || !principal->GetIsContentPrincipal() ||
|
|
principal->GetIsInPrivateBrowsing()) {
|
|
aRv.ThrowSecurityError("Cannot create MLS store for origin");
|
|
return nullptr;
|
|
}
|
|
|
|
// Create the endpoints for the MLS actor
|
|
mozilla::ipc::Endpoint<PMLSTransactionParent> parentEndpoint;
|
|
mozilla::ipc::Endpoint<PMLSTransactionChild> childEndpoint;
|
|
MOZ_ALWAYS_SUCCEEDS(
|
|
PMLSTransaction::CreateEndpoints(&parentEndpoint, &childEndpoint));
|
|
|
|
mozilla::ipc::PBackgroundChild* backgroundChild =
|
|
mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread();
|
|
if (!backgroundChild) {
|
|
aRv.Throw(NS_ERROR_UNEXPECTED);
|
|
return nullptr;
|
|
}
|
|
|
|
// Bind the child actor, and send the parent endpoint.
|
|
RefPtr<MLSTransactionChild> actor = new MLSTransactionChild();
|
|
MOZ_ALWAYS_TRUE(childEndpoint.Bind(actor));
|
|
|
|
MOZ_ALWAYS_TRUE(backgroundChild->SendCreateMLSTransaction(
|
|
std::move(parentEndpoint), WrapNotNull(principal)));
|
|
|
|
return MakeAndAddRef<MLS>(global, actor);
|
|
}
|
|
|
|
MLS::MLS(nsIGlobalObject* aGlobalObject, MLSTransactionChild* aActor)
|
|
: mGlobalObject(aGlobalObject), mTransactionChild(aActor) {
|
|
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug, ("MLS::MLS()"));
|
|
}
|
|
|
|
MLS::~MLS() {
|
|
if (mTransactionChild) {
|
|
mTransactionChild->Close();
|
|
}
|
|
}
|
|
|
|
JSObject* MLS::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
|
|
return MLS_Binding::Wrap(aCx, this, aGivenProto);
|
|
}
|
|
|
|
//
|
|
// API
|
|
//
|
|
|
|
already_AddRefed<Promise> MLS::DeleteState(ErrorResult& aRv) {
|
|
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug, ("MLS::DeleteState()"));
|
|
|
|
// Create a new Promise object for the result
|
|
RefPtr<Promise> promise = Promise::Create(mGlobalObject, aRv);
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
return nullptr;
|
|
}
|
|
|
|
mTransactionChild->SendRequestStateDelete(
|
|
[promise](bool result) {
|
|
if (result) {
|
|
promise->MaybeResolveWithUndefined();
|
|
} else {
|
|
promise->MaybeReject(NS_ERROR_FAILURE);
|
|
}
|
|
},
|
|
[promise](::mozilla::ipc::ResponseRejectReason) {
|
|
promise->MaybeRejectWithUnknownError("deleteState failed");
|
|
});
|
|
|
|
return promise.forget();
|
|
}
|
|
|
|
already_AddRefed<Promise> MLS::GenerateIdentity(ErrorResult& aRv) {
|
|
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug, ("MLS::GenerateIdentity()"));
|
|
|
|
// Create a new Promise object for the result
|
|
RefPtr<Promise> promise = Promise::Create(mGlobalObject, aRv);
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
return nullptr;
|
|
}
|
|
|
|
mTransactionChild->SendRequestGenerateIdentityKeypair()->Then(
|
|
GetCurrentSerialEventTarget(), __func__,
|
|
[promise, self = RefPtr{this}](Maybe<RawBytes>&& result) {
|
|
// Check if the value is Nothing
|
|
if (result.isNothing()) {
|
|
promise->MaybeRejectWithUnknownError(
|
|
"generateIdentityKeypair failed");
|
|
return;
|
|
}
|
|
|
|
// Get the context from the GlobalObject
|
|
AutoJSAPI jsapi;
|
|
if (NS_WARN_IF(!jsapi.Init(self->mGlobalObject))) {
|
|
promise->MaybeRejectWithUnknownError(
|
|
"generateIdentityKeypair failed");
|
|
return;
|
|
}
|
|
JSContext* cx = jsapi.cx();
|
|
|
|
// Construct the Uint8Array object
|
|
ErrorResult error;
|
|
JS::Rooted<JSObject*> content(
|
|
cx, Uint8Array::Create(cx, result->data(), error));
|
|
error.WouldReportJSException();
|
|
if (error.Failed()) {
|
|
promise->MaybeReject(std::move(error));
|
|
return;
|
|
}
|
|
|
|
// Construct MLSBytes with the client identifer as content
|
|
RootedDictionary<MLSBytes> rvalue(cx);
|
|
rvalue.mType = MLSObjectType::Client_identifier;
|
|
rvalue.mContent.Init(content);
|
|
|
|
// Resolve the promise with the MLSBytes object
|
|
promise->MaybeResolve(rvalue);
|
|
},
|
|
[promise](::mozilla::ipc::ResponseRejectReason aReason) {
|
|
promise->MaybeRejectWithUnknownError("generateIdentity failed");
|
|
});
|
|
|
|
return promise.forget();
|
|
}
|
|
|
|
already_AddRefed<Promise> MLS::GenerateCredential(
|
|
const MLSBytesOrUint8ArrayOrUTF8String& aJsCredContent, ErrorResult& aRv) {
|
|
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug,
|
|
("MLS::GenerateCredentialBasic()"));
|
|
|
|
// Handle the credential content parameter
|
|
nsTArray<uint8_t> credContent = ExtractMLSBytesOrUint8ArrayOrUTF8String(
|
|
MLSObjectType::Credential_basic, aJsCredContent, aRv);
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
return nullptr;
|
|
}
|
|
|
|
// Check if the credContent is empty
|
|
if (NS_WARN_IF(credContent.IsEmpty())) {
|
|
aRv.ThrowTypeError("The credential content must not be empty");
|
|
return nullptr;
|
|
}
|
|
|
|
// Create a new Promise object for the result
|
|
RefPtr<Promise> promise = Promise::Create(mGlobalObject, aRv);
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
return nullptr;
|
|
}
|
|
|
|
mTransactionChild->SendRequestGenerateCredentialBasic(credContent)
|
|
->Then(
|
|
GetCurrentSerialEventTarget(), __func__,
|
|
[promise, self = RefPtr{this}](Maybe<RawBytes>&& result) {
|
|
// Check if the value is Nothing
|
|
if (result.isNothing()) {
|
|
promise->MaybeRejectWithUnknownError(
|
|
"generateCredentialBasic failed");
|
|
return;
|
|
}
|
|
|
|
// Get the context from the GlobalObject
|
|
AutoJSAPI jsapi;
|
|
if (NS_WARN_IF(!jsapi.Init(self->mGlobalObject))) {
|
|
promise->MaybeRejectWithUnknownError(
|
|
"generateCredentialBasic failed");
|
|
return;
|
|
}
|
|
JSContext* cx = jsapi.cx();
|
|
|
|
// Construct the Uint8Array object
|
|
ErrorResult error;
|
|
JS::Rooted<JSObject*> content(
|
|
cx, Uint8Array::Create(cx, result->data(), error));
|
|
error.WouldReportJSException();
|
|
if (error.Failed()) {
|
|
promise->MaybeReject(std::move(error));
|
|
return;
|
|
}
|
|
|
|
// Construct MLSBytes with the client identifer as content
|
|
RootedDictionary<MLSBytes> rvalue(cx);
|
|
rvalue.mType = MLSObjectType::Credential_basic;
|
|
rvalue.mContent.Init(content);
|
|
|
|
// Resolve the promise
|
|
promise->MaybeResolve(rvalue);
|
|
},
|
|
[promise](::mozilla::ipc::ResponseRejectReason aReason) {
|
|
promise->MaybeRejectWithUnknownError(
|
|
"generateCredentialBasic failed");
|
|
});
|
|
|
|
return promise.forget();
|
|
}
|
|
|
|
already_AddRefed<Promise> MLS::GenerateKeyPackage(
|
|
const MLSBytesOrUint8Array& aJsClientIdentifier,
|
|
const MLSBytesOrUint8Array& aJsCredential, ErrorResult& aRv) {
|
|
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug, ("MLS::GenerateKeyPackage()"));
|
|
|
|
// Handle the client identifier parameter
|
|
nsTArray<uint8_t> clientIdentifier = ExtractMLSBytesOrUint8Array(
|
|
MLSObjectType::Client_identifier, aJsClientIdentifier, aRv);
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
return nullptr;
|
|
}
|
|
|
|
// Check if the client identifier is empty
|
|
if (NS_WARN_IF(clientIdentifier.IsEmpty())) {
|
|
aRv.ThrowTypeError("The client identifier must not be empty");
|
|
return nullptr;
|
|
}
|
|
|
|
// Handle the credential parameter
|
|
nsTArray<uint8_t> credential = ExtractMLSBytesOrUint8Array(
|
|
MLSObjectType::Credential_basic, aJsCredential, aRv);
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
return nullptr;
|
|
}
|
|
|
|
// Check if the credential is empty
|
|
if (NS_WARN_IF(credential.IsEmpty())) {
|
|
aRv.ThrowTypeError("The credential must not be empty");
|
|
return nullptr;
|
|
}
|
|
|
|
// Create a new Promise object for the result
|
|
RefPtr<Promise> promise = Promise::Create(mGlobalObject, aRv);
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
return nullptr;
|
|
}
|
|
|
|
// Use the static method or instance to send the IPC message
|
|
mTransactionChild->SendRequestGenerateKeyPackage(clientIdentifier, credential)
|
|
->Then(
|
|
GetCurrentSerialEventTarget(), __func__,
|
|
[promise, self = RefPtr{this}](Maybe<RawBytes>&& keyPackage) {
|
|
// Check if the value is Nothing
|
|
if (keyPackage.isNothing()) {
|
|
promise->MaybeReject(NS_ERROR_FAILURE);
|
|
return;
|
|
}
|
|
|
|
// Get the context from the GlobalObject
|
|
AutoJSAPI jsapi;
|
|
if (NS_WARN_IF(!jsapi.Init(self->mGlobalObject))) {
|
|
promise->MaybeReject(NS_ERROR_FAILURE);
|
|
return;
|
|
}
|
|
JSContext* cx = jsapi.cx();
|
|
|
|
// Construct the Uint8Array object
|
|
ErrorResult error;
|
|
JS::Rooted<JSObject*> content(
|
|
cx, Uint8Array::Create(cx, keyPackage->data(), error));
|
|
error.WouldReportJSException();
|
|
if (error.Failed()) {
|
|
promise->MaybeReject(std::move(error));
|
|
return;
|
|
}
|
|
|
|
// Construct MLSBytes with the client identifer as content
|
|
RootedDictionary<MLSBytes> rvalue(cx);
|
|
rvalue.mType = MLSObjectType::Key_package;
|
|
rvalue.mContent.Init(content);
|
|
|
|
// Resolve the promise
|
|
promise->MaybeResolve(rvalue);
|
|
},
|
|
[promise](::mozilla::ipc::ResponseRejectReason aReason) {
|
|
promise->MaybeRejectWithUnknownError("generateKeyPackage failed");
|
|
});
|
|
|
|
return promise.forget();
|
|
}
|
|
|
|
already_AddRefed<Promise> MLS::GroupCreate(
|
|
const MLSBytesOrUint8Array& aJsClientIdentifier,
|
|
const MLSBytesOrUint8Array& aJsCredential, ErrorResult& aRv) {
|
|
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug, ("MLS::GroupCreate()"));
|
|
|
|
// Handle the client identifier parameter
|
|
nsTArray<uint8_t> clientIdentifier = ExtractMLSBytesOrUint8Array(
|
|
MLSObjectType::Client_identifier, aJsClientIdentifier, aRv);
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
return nullptr;
|
|
}
|
|
|
|
// Check if the client identifier is empty
|
|
if (NS_WARN_IF(clientIdentifier.IsEmpty())) {
|
|
aRv.ThrowTypeError("The client identifier must not be empty");
|
|
return nullptr;
|
|
}
|
|
|
|
// Handle the credential parameter
|
|
nsTArray<uint8_t> credential = ExtractMLSBytesOrUint8Array(
|
|
MLSObjectType::Credential_basic, aJsCredential, aRv);
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
return nullptr;
|
|
}
|
|
|
|
// Check if the credential is empty
|
|
if (NS_WARN_IF(credential.IsEmpty())) {
|
|
aRv.ThrowTypeError("The credential must not be empty");
|
|
return nullptr;
|
|
}
|
|
|
|
// Log the hex of clientIdentifier
|
|
if (MOZ_LOG_TEST(gMlsLog, LogLevel::Debug)) {
|
|
nsAutoCString clientIdHex;
|
|
for (uint8_t byte : clientIdentifier) {
|
|
clientIdHex.AppendPrintf("%02X", byte);
|
|
}
|
|
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug,
|
|
("clientIdentifier in hex: %s\n", clientIdHex.get()));
|
|
}
|
|
|
|
// Initialize jsGroupIdentifier to one byte of value 0xFF.
|
|
// We do not want to allow choosing the GID at this point.
|
|
// This value not being of the correct length will be discarded
|
|
// internally and a fresh GID will be generated.
|
|
//
|
|
// In the future, the caller will allow choosing the GID.
|
|
AutoTArray<uint8_t, 1> groupIdentifier{0xFF};
|
|
|
|
// Create a new Promise object for the result
|
|
RefPtr<Promise> promise = Promise::Create(mGlobalObject, aRv);
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
return nullptr;
|
|
}
|
|
|
|
// Use the static method or instance to send the IPC message
|
|
mTransactionChild
|
|
->SendRequestGroupCreate(clientIdentifier, credential, groupIdentifier)
|
|
->Then(
|
|
GetCurrentSerialEventTarget(), __func__,
|
|
[promise, self = RefPtr{this},
|
|
clientIdentifier(std::move(clientIdentifier))](
|
|
Maybe<mozilla::security::mls::GkGroupIdEpoch>&&
|
|
groupIdEpoch) mutable {
|
|
// Check if the value is Nothing
|
|
if (groupIdEpoch.isNothing()) {
|
|
promise->MaybeReject(NS_ERROR_FAILURE);
|
|
return;
|
|
}
|
|
|
|
RefPtr<MLSGroupView> group =
|
|
new MLSGroupView(self, std::move(groupIdEpoch->group_id),
|
|
std::move(clientIdentifier));
|
|
|
|
promise->MaybeResolve(group);
|
|
},
|
|
[promise](::mozilla::ipc::ResponseRejectReason aReason) {
|
|
MOZ_LOG(gMlsLog, mozilla::LogLevel::Error,
|
|
("IPC message rejected with reason: %d",
|
|
static_cast<int>(aReason)));
|
|
promise->MaybeRejectWithUnknownError("groupCreate failed");
|
|
});
|
|
|
|
return promise.forget();
|
|
}
|
|
|
|
already_AddRefed<mozilla::dom::Promise> MLS::GroupGet(
|
|
const MLSBytesOrUint8Array& aJsGroupIdentifier,
|
|
const MLSBytesOrUint8Array& aJsClientIdentifier, ErrorResult& aRv) {
|
|
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug, ("MLS::GroupGet()"));
|
|
|
|
// Handle the group identifier parameter
|
|
nsTArray<uint8_t> groupIdentifier = ExtractMLSBytesOrUint8Array(
|
|
MLSObjectType::Group_identifier, aJsGroupIdentifier, aRv);
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
return nullptr;
|
|
}
|
|
|
|
// Check if the group identifier is empty
|
|
if (NS_WARN_IF(groupIdentifier.IsEmpty())) {
|
|
aRv.ThrowTypeError("The group identifier must not be empty");
|
|
return nullptr;
|
|
}
|
|
|
|
// Handle the client identifier parameter
|
|
nsTArray<uint8_t> clientIdentifier = ExtractMLSBytesOrUint8Array(
|
|
MLSObjectType::Client_identifier, aJsClientIdentifier, aRv);
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
return nullptr;
|
|
}
|
|
|
|
// Check if the client identifier is empty
|
|
if (NS_WARN_IF(clientIdentifier.IsEmpty())) {
|
|
aRv.ThrowTypeError("The client identifier must not be empty");
|
|
return nullptr;
|
|
}
|
|
|
|
// Create a new Promise object for the result
|
|
RefPtr<Promise> promise = Promise::Create(mGlobalObject, aRv);
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
return nullptr;
|
|
}
|
|
|
|
// Initialize label, context and len
|
|
// We pass this through IPC to be able to reuse the same code for different
|
|
// labels in the future
|
|
AutoTArray<uint8_t, 7> label{'l', 'i', 'v', 'e', 'n', 'e', 's', 's'};
|
|
AutoTArray<uint8_t, 1> context{0x00};
|
|
uint64_t len = 32;
|
|
|
|
// Use the static method or instance to send the IPC message
|
|
mTransactionChild
|
|
->SendRequestExportSecret(groupIdentifier, clientIdentifier, label,
|
|
context, len)
|
|
->Then(
|
|
GetCurrentSerialEventTarget(), __func__,
|
|
[promise, self = RefPtr{this},
|
|
groupIdentifier(std::move(groupIdentifier)),
|
|
clientIdentifier(std::move(clientIdentifier))](
|
|
Maybe<mozilla::security::mls::GkExporterOutput>&&
|
|
exporterOutput) mutable {
|
|
// Check if the exporterOutput contains a value
|
|
if (exporterOutput.isNothing()) {
|
|
promise->MaybeReject(NS_ERROR_FAILURE);
|
|
return;
|
|
}
|
|
|
|
RefPtr<MLSGroupView> group =
|
|
new MLSGroupView(self, std::move(exporterOutput->group_id),
|
|
std::move(clientIdentifier));
|
|
promise->MaybeResolve(group);
|
|
},
|
|
[promise](::mozilla::ipc::ResponseRejectReason aReason) {
|
|
promise->MaybeRejectWithUnknownError("exportSecret failed");
|
|
});
|
|
|
|
return promise.forget();
|
|
}
|
|
|
|
already_AddRefed<Promise> MLS::GroupJoin(
|
|
const MLSBytesOrUint8Array& aJsClientIdentifier,
|
|
const MLSBytesOrUint8Array& aJsWelcome, ErrorResult& aRv) {
|
|
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug, ("MLS::GroupJoin()"));
|
|
|
|
// Handle the client identifier parameter
|
|
nsTArray<uint8_t> clientIdentifier = ExtractMLSBytesOrUint8Array(
|
|
MLSObjectType::Client_identifier, aJsClientIdentifier, aRv);
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
return nullptr;
|
|
}
|
|
|
|
// Check if the client identifier is empty
|
|
if (NS_WARN_IF(clientIdentifier.IsEmpty())) {
|
|
aRv.ThrowTypeError("The client identifier must not be empty");
|
|
return nullptr;
|
|
}
|
|
|
|
// Handle the welcome parameter
|
|
nsTArray<uint8_t> welcome =
|
|
ExtractMLSBytesOrUint8Array(MLSObjectType::Welcome, aJsWelcome, aRv);
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
return nullptr;
|
|
}
|
|
|
|
// Check if the welcome is empty
|
|
if (NS_WARN_IF(welcome.IsEmpty())) {
|
|
aRv.ThrowTypeError("The welcome must not be empty");
|
|
return nullptr;
|
|
}
|
|
|
|
// Create a new Promise object for the result
|
|
RefPtr<Promise> promise = Promise::Create(mGlobalObject, aRv);
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
return nullptr;
|
|
}
|
|
|
|
mTransactionChild->SendRequestGroupJoin(clientIdentifier, welcome)
|
|
->Then(
|
|
GetCurrentSerialEventTarget(), __func__,
|
|
[promise, self = RefPtr{this},
|
|
clientIdentifier(std::move(clientIdentifier))](
|
|
Maybe<mozilla::security::mls::GkGroupIdEpoch>&&
|
|
groupIdEpoch) mutable {
|
|
// Check if the value is Nothing
|
|
if (groupIdEpoch.isNothing()) {
|
|
promise->MaybeReject(NS_ERROR_FAILURE);
|
|
return;
|
|
}
|
|
|
|
// Returns groupId and epoch
|
|
RefPtr<MLSGroupView> group =
|
|
new MLSGroupView(self, std::move(groupIdEpoch->group_id),
|
|
std::move(clientIdentifier));
|
|
|
|
// Resolve the promise
|
|
promise->MaybeResolve(group);
|
|
},
|
|
[promise](::mozilla::ipc::ResponseRejectReason aReason) {
|
|
promise->MaybeRejectWithUnknownError("groupJoin failed");
|
|
});
|
|
|
|
return promise.forget();
|
|
}
|
|
|
|
already_AddRefed<Promise> MLS::GetGroupIdFromMessage(
|
|
const MLSBytesOrUint8Array& aJsMessage, ErrorResult& aRv) {
|
|
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug, ("MLS::GetGroupIdFromMessage()"));
|
|
|
|
// Handle the message parameter
|
|
nsTArray<uint8_t> message =
|
|
ExtractMLSBytesOrUint8ArrayWithUnknownType(aJsMessage, aRv);
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
return nullptr;
|
|
}
|
|
|
|
// Check if the message is empty
|
|
if (NS_WARN_IF(message.IsEmpty())) {
|
|
aRv.ThrowTypeError("The message must not be empty");
|
|
return nullptr;
|
|
}
|
|
|
|
// Create a new Promise object for the result
|
|
RefPtr<Promise> promise = Promise::Create(mGlobalObject, aRv);
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
return nullptr;
|
|
}
|
|
|
|
mTransactionChild->SendRequestGetGroupIdentifier(message)->Then(
|
|
GetCurrentSerialEventTarget(), __func__,
|
|
[promise, self = RefPtr{this},
|
|
message(std::move(message))](Maybe<RawBytes>&& result) {
|
|
// Check if the value is Nothing
|
|
if (result.isNothing()) {
|
|
promise->MaybeReject(NS_ERROR_FAILURE);
|
|
return;
|
|
}
|
|
|
|
// Get the context from the GlobalObject
|
|
AutoJSAPI jsapi;
|
|
if (NS_WARN_IF(!jsapi.Init(self->mGlobalObject))) {
|
|
MOZ_LOG(gMlsLog, mozilla::LogLevel::Error,
|
|
("Failed to initialize JSAPI"));
|
|
promise->MaybeReject(NS_ERROR_FAILURE);
|
|
return;
|
|
}
|
|
JSContext* cx = jsapi.cx();
|
|
|
|
// Construct the Uint8Array objects based on the tag
|
|
ErrorResult error;
|
|
JS::Rooted<JSObject*> jsGroupId(
|
|
cx, Uint8Array::Create(cx, result->data(), error));
|
|
error.WouldReportJSException();
|
|
if (error.Failed()) {
|
|
promise->MaybeReject(std::move(error));
|
|
return;
|
|
}
|
|
|
|
// Construct the MLSBytes object for the groupId
|
|
RootedDictionary<MLSBytes> rvalue(cx);
|
|
rvalue.mType = MLSObjectType::Group_identifier;
|
|
rvalue.mContent.Init(jsGroupId);
|
|
|
|
// Log if in debug mode
|
|
if (MOZ_LOG_TEST(gMlsLog, LogLevel::Debug)) {
|
|
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug,
|
|
("Successfully constructed MLSBytes"));
|
|
}
|
|
|
|
// Resolve the promise
|
|
promise->MaybeResolve(rvalue);
|
|
},
|
|
[promise](::mozilla::ipc::ResponseRejectReason aReason) {
|
|
MOZ_LOG(
|
|
gMlsLog, mozilla::LogLevel::Error,
|
|
("IPC call rejected with reason: %d", static_cast<int>(aReason)));
|
|
promise->MaybeRejectWithUnknownError("getGroupIdFromMessage failed");
|
|
});
|
|
|
|
return promise.forget();
|
|
}
|
|
|
|
already_AddRefed<Promise> MLS::GetGroupEpochFromMessage(
|
|
const MLSBytesOrUint8Array& aJsMessage, ErrorResult& aRv) {
|
|
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug,
|
|
("MLS::GetGroupEpochFromMessage()"));
|
|
|
|
// Handle the message parameter
|
|
nsTArray<uint8_t> message =
|
|
ExtractMLSBytesOrUint8ArrayWithUnknownType(aJsMessage, aRv);
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
return nullptr;
|
|
}
|
|
|
|
// Check if the message is empty
|
|
if (NS_WARN_IF(message.IsEmpty())) {
|
|
aRv.ThrowTypeError("The message must not be empty");
|
|
return nullptr;
|
|
}
|
|
|
|
// Create a new Promise object for the result
|
|
RefPtr<Promise> promise = Promise::Create(mGlobalObject, aRv);
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
return nullptr;
|
|
}
|
|
|
|
mTransactionChild->SendRequestGetGroupEpoch(message)->Then(
|
|
GetCurrentSerialEventTarget(), __func__,
|
|
[promise, self = RefPtr{this},
|
|
message(std::move(message))](Maybe<RawBytes>&& result) {
|
|
// Check if the value is Nothing
|
|
if (result.isNothing()) {
|
|
promise->MaybeReject(NS_ERROR_FAILURE);
|
|
return;
|
|
}
|
|
|
|
// Get the context from the GlobalObject
|
|
AutoJSAPI jsapi;
|
|
if (NS_WARN_IF(!jsapi.Init(self->mGlobalObject))) {
|
|
MOZ_LOG(gMlsLog, mozilla::LogLevel::Error,
|
|
("Failed to initialize JSAPI"));
|
|
promise->MaybeReject(NS_ERROR_FAILURE);
|
|
return;
|
|
}
|
|
JSContext* cx = jsapi.cx();
|
|
|
|
// Construct the Uint8Array objects based on the tag
|
|
ErrorResult error;
|
|
JS::Rooted<JSObject*> jsGroupId(
|
|
cx, Uint8Array::Create(cx, result->data(), error));
|
|
error.WouldReportJSException();
|
|
if (error.Failed()) {
|
|
promise->MaybeReject(std::move(error));
|
|
return;
|
|
}
|
|
|
|
// Construct the MLSBytes object for the groupId
|
|
RootedDictionary<MLSBytes> rvalue(cx);
|
|
rvalue.mType = MLSObjectType::Group_epoch;
|
|
rvalue.mContent.Init(jsGroupId);
|
|
|
|
// Log if in debug mode
|
|
if (MOZ_LOG_TEST(gMlsLog, LogLevel::Debug)) {
|
|
MOZ_LOG(gMlsLog, mozilla::LogLevel::Debug,
|
|
("Successfully constructed MLSBytes"));
|
|
}
|
|
|
|
// Resolve the promise
|
|
promise->MaybeResolve(rvalue);
|
|
},
|
|
[promise](::mozilla::ipc::ResponseRejectReason aReason) {
|
|
MOZ_LOG(
|
|
gMlsLog, mozilla::LogLevel::Error,
|
|
("IPC call rejected with reason: %d", static_cast<int>(aReason)));
|
|
promise->MaybeRejectWithUnknownError("getGroupEpochFromMessage failed");
|
|
});
|
|
|
|
return promise.forget();
|
|
}
|
|
|
|
} // namespace mozilla::dom
|