summaryrefslogtreecommitdiffstats
path: root/dom/canvas/QueueParamTraits.h
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /dom/canvas/QueueParamTraits.h
parentInitial commit. (diff)
downloadfirefox-upstream.tar.xz
firefox-upstream.zip
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/canvas/QueueParamTraits.h')
-rw-r--r--dom/canvas/QueueParamTraits.h890
1 files changed, 890 insertions, 0 deletions
diff --git a/dom/canvas/QueueParamTraits.h b/dom/canvas/QueueParamTraits.h
new file mode 100644
index 0000000000..fbb6bce6f8
--- /dev/null
+++ b/dom/canvas/QueueParamTraits.h
@@ -0,0 +1,890 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=4 et :
+ */
+/* 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/. */
+
+#ifndef _QUEUEPARAMTRAITS_H_
+#define _QUEUEPARAMTRAITS_H_ 1
+
+#include "ipc/EnumSerializer.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/IntegerRange.h"
+#include "mozilla/ipc/ProtocolUtils.h"
+#include "mozilla/ipc/SharedMemoryBasic.h"
+#include "mozilla/ipc/Shmem.h"
+#include "mozilla/Logging.h"
+#include "mozilla/TimeStamp.h"
+#include "mozilla/TypeTraits.h"
+#include "nsExceptionHandler.h"
+#include "nsString.h"
+#include "WebGLTypes.h"
+
+namespace mozilla {
+namespace webgl {
+
+enum class QueueStatus {
+ // Operation was successful
+ kSuccess,
+ // The operation failed because the queue isn't ready for it.
+ // Either the queue is too full for an insert or too empty for a remove.
+ // The operation may succeed if retried.
+ kNotReady,
+ // The operation required more room than the queue supports.
+ // It should not be retried -- it will always fail.
+ kTooSmall,
+ // The operation failed for some reason that is unrecoverable.
+ // All values below this value indicate a fata error.
+ kFatalError,
+ // Fatal error: Internal processing ran out of memory. This is likely e.g.
+ // during de-serialization.
+ kOOMError,
+};
+
+inline bool IsSuccess(QueueStatus status) {
+ return status == QueueStatus::kSuccess;
+}
+
+inline bool operator!(const QueueStatus status) { return !IsSuccess(status); }
+
+template <typename T>
+struct RemoveCVR {
+ typedef typename std::remove_reference<typename std::remove_cv<T>::type>::type
+ Type;
+};
+
+inline size_t UsedBytes(size_t aQueueBufferSize, size_t aRead, size_t aWrite) {
+ return (aRead <= aWrite) ? aWrite - aRead
+ : (aQueueBufferSize - aRead) + aWrite;
+}
+
+inline size_t FreeBytes(size_t aQueueBufferSize, size_t aRead, size_t aWrite) {
+ // Remember, queueSize is queueBufferSize-1
+ return (aQueueBufferSize - 1) - UsedBytes(aQueueBufferSize, aRead, aWrite);
+}
+
+template <typename T>
+struct IsTriviallySerializable
+ : public std::integral_constant<bool, std::is_arithmetic<T>::value &&
+ !std::is_same<T, bool>::value> {};
+
+/**
+ * QueueParamTraits provide the user with a way to implement PCQ argument
+ * (de)serialization. It uses a PcqView, which permits the system to
+ * abandon all changes to the underlying PCQ if any operation fails.
+ *
+ * The transactional nature of PCQ operations make the ideal behavior a bit
+ * complex. Since the PCQ has a fixed amount of memory available to it,
+ * TryInsert operations operations are expected to sometimes fail and be
+ * re-issued later. We want these failures to be inexpensive. The same
+ * goes for TryRemove, which fails when there isn't enough data in
+ * the queue yet for them to complete.
+ *
+ * Their expected interface is:
+ *
+ * template<> struct QueueParamTraits<typename RemoveCVR<Arg>::Type> {
+ * // Write data from aArg into the PCQ.
+ * static QueueStatus Write(ProducerView& aProducerView, const Arg& aArg)
+ * {...};
+ *
+ * // Read data from the PCQ into aArg, or just skip the data if aArg is null.
+ * static QueueStatus Read(ConsumerView& aConsumerView, Arg* aArg) {...}
+ * };
+ */
+template <typename Arg>
+struct QueueParamTraits; // Todo: s/QueueParamTraits/SizedParamTraits/
+
+/**
+ * The marshaller handles all data insertion into the queue.
+ */
+class Marshaller {
+ public:
+ static QueueStatus WriteObject(uint8_t* aQueue, size_t aQueueBufferSize,
+ size_t aRead, size_t* aWrite, const void* aArg,
+ size_t aArgLength) {
+ const uint8_t* buf = reinterpret_cast<const uint8_t*>(aArg);
+ if (FreeBytes(aQueueBufferSize, aRead, *aWrite) < aArgLength) {
+ return QueueStatus::kNotReady;
+ }
+
+ if (*aWrite + aArgLength <= aQueueBufferSize) {
+ memcpy(aQueue + *aWrite, buf, aArgLength);
+ } else {
+ size_t firstLen = aQueueBufferSize - *aWrite;
+ memcpy(aQueue + *aWrite, buf, firstLen);
+ memcpy(aQueue, &buf[firstLen], aArgLength - firstLen);
+ }
+ *aWrite = (*aWrite + aArgLength) % aQueueBufferSize;
+ return QueueStatus::kSuccess;
+ }
+
+ static QueueStatus ReadObject(const uint8_t* aQueue, size_t aQueueBufferSize,
+ size_t* aRead, size_t aWrite, void* aArg,
+ size_t aArgLength) {
+ if (UsedBytes(aQueueBufferSize, *aRead, aWrite) < aArgLength) {
+ return QueueStatus::kNotReady;
+ }
+
+ if (aArg) {
+ uint8_t* buf = reinterpret_cast<uint8_t*>(aArg);
+ if (*aRead + aArgLength <= aQueueBufferSize) {
+ memcpy(buf, aQueue + *aRead, aArgLength);
+ } else {
+ size_t firstLen = aQueueBufferSize - *aRead;
+ memcpy(buf, aQueue + *aRead, firstLen);
+ memcpy(&buf[firstLen], aQueue, aArgLength - firstLen);
+ }
+ }
+
+ *aRead = (*aRead + aArgLength) % aQueueBufferSize;
+ return QueueStatus::kSuccess;
+ }
+};
+
+template <typename T>
+inline Range<T> AsRange(T* const begin, T* const end) {
+ const auto size = MaybeAs<size_t>(end - begin);
+ MOZ_RELEASE_ASSERT(size);
+ return {begin, *size};
+}
+
+/**
+ * Used to give QueueParamTraits a way to write to the Producer without
+ * actually altering it, in case the transaction fails.
+ * THis object maintains the error state of the transaction and
+ * discards commands issued after an error is encountered.
+ */
+template <typename _Producer>
+class ProducerView {
+ public:
+ using Producer = _Producer;
+
+ ProducerView(Producer* aProducer, size_t aRead, size_t* aWrite)
+ : mProducer(aProducer),
+ mRead(aRead),
+ mWrite(aWrite),
+ mStatus(QueueStatus::kSuccess) {}
+
+ template <typename T>
+ QueueStatus WriteFromRange(const Range<const T>& src) {
+ if (!mStatus) return mStatus;
+ mProducer->WriteFromRange(src);
+ return mStatus;
+ }
+ /**
+ * Copy bytes from aBuffer to the producer if there is enough room.
+ * aBufferSize must not be 0.
+ */
+ template <typename T>
+ inline QueueStatus Write(const T* begin, const T* end) {
+ MOZ_RELEASE_ASSERT(begin <= end);
+ if (!mStatus) return mStatus;
+ WriteFromRange(AsRange(begin, end));
+ return mStatus;
+ }
+
+ template <typename T>
+ inline QueueStatus WritePod(const T& in) {
+ static_assert(std::is_trivially_copyable_v<T>);
+ const auto begin = reinterpret_cast<const uint8_t*>(&in);
+ return Write(begin, begin + sizeof(T));
+ }
+
+ /**
+ * Serialize aArg using Arg's QueueParamTraits.
+ */
+ template <typename Arg>
+ QueueStatus WriteParam(const Arg& aArg) {
+ return mozilla::webgl::QueueParamTraits<
+ typename RemoveCVR<Arg>::Type>::Write(*this, aArg);
+ }
+
+ QueueStatus GetStatus() { return mStatus; }
+
+ private:
+ Producer* mProducer;
+ size_t mRead;
+ size_t* mWrite;
+ QueueStatus mStatus;
+};
+
+/**
+ * Used to give QueueParamTraits a way to read from the Consumer without
+ * actually altering it, in case the transaction fails.
+ */
+template <typename _Consumer>
+class ConsumerView {
+ public:
+ using Consumer = _Consumer;
+
+ ConsumerView(Consumer* aConsumer, size_t* aRead, size_t aWrite)
+ : mConsumer(aConsumer),
+ mRead(aRead),
+ mWrite(aWrite),
+ mStatus(QueueStatus::kSuccess) {}
+
+ /**
+ * Read bytes from the consumer if there is enough data. aBuffer may
+ * be null (in which case the data is skipped)
+ */
+ template <typename T>
+ inline QueueStatus Read(T* const destBegin, T* const destEnd) {
+ MOZ_ASSERT(destBegin);
+ MOZ_RELEASE_ASSERT(destBegin <= destEnd);
+ if (!mStatus) return mStatus;
+
+ const auto dest = AsRange(destBegin, destEnd);
+ const auto view = ReadRange<T>(dest.length());
+ if (!view) return mStatus;
+ const auto byteSize = ByteSize(dest);
+ if (byteSize) {
+ memcpy(dest.begin().get(), view->begin().get(), byteSize);
+ }
+ return mStatus;
+ }
+
+ /// Return a view wrapping the shmem.
+ template <typename T>
+ inline Maybe<Range<const T>> ReadRange(const size_t elemCount) {
+ if (!mStatus) return {};
+ const auto view = mConsumer->template ReadRange<T>(elemCount);
+ if (!view) {
+ mStatus = QueueStatus::kTooSmall;
+ }
+ return view;
+ }
+
+ template <typename T>
+ inline QueueStatus ReadPod(T* out) {
+ static_assert(std::is_trivially_copyable_v<T>);
+ const auto begin = reinterpret_cast<uint8_t*>(out);
+ return Read(begin, begin + sizeof(T));
+ }
+
+ /**
+ * Deserialize aArg using Arg's QueueParamTraits.
+ * If the return value is not Success then aArg is not changed.
+ */
+ template <typename Arg>
+ QueueStatus ReadParam(Arg* aArg) {
+ MOZ_ASSERT(aArg);
+ return mozilla::webgl::QueueParamTraits<std::remove_cv_t<Arg>>::Read(*this,
+ aArg);
+ }
+
+ QueueStatus GetStatus() { return mStatus; }
+
+ private:
+ Consumer* mConsumer;
+ size_t* mRead;
+ size_t mWrite;
+ QueueStatus mStatus;
+};
+
+// ---------------------------------------------------------------
+
+/**
+ * True for types that can be (de)serialized by memcpy.
+ */
+template <typename Arg>
+struct QueueParamTraits {
+ template <typename U>
+ static QueueStatus Write(ProducerView<U>& aProducerView, const Arg& aArg) {
+ static_assert(mozilla::webgl::template IsTriviallySerializable<Arg>::value,
+ "No QueueParamTraits specialization was found for this type "
+ "and it does not satisfy IsTriviallySerializable.");
+ // Write self as binary
+ const auto begin = &aArg;
+ return aProducerView.Write(begin, begin + 1);
+ }
+
+ template <typename U>
+ static QueueStatus Read(ConsumerView<U>& aConsumerView, Arg* aArg) {
+ static_assert(mozilla::webgl::template IsTriviallySerializable<Arg>::value,
+ "No QueueParamTraits specialization was found for this type "
+ "and it does not satisfy IsTriviallySerializable.");
+ // Read self as binary
+ return aConsumerView.Read(aArg, aArg + 1);
+ }
+};
+
+// ---------------------------------------------------------------
+
+template <>
+struct QueueParamTraits<bool> {
+ using ParamType = bool;
+
+ template <typename U>
+ static QueueStatus Write(ProducerView<U>& aProducerView,
+ const ParamType& aArg) {
+ uint8_t temp = aArg ? 1 : 0;
+ return aProducerView.WriteParam(temp);
+ }
+
+ template <typename U>
+ static QueueStatus Read(ConsumerView<U>& aConsumerView, ParamType* aArg) {
+ uint8_t temp;
+ if (IsSuccess(aConsumerView.ReadParam(&temp))) {
+ MOZ_ASSERT(temp == 1 || temp == 0);
+ *aArg = temp ? true : false;
+ }
+ return aConsumerView.GetStatus();
+ }
+};
+
+// ---------------------------------------------------------------
+
+// Adapted from IPC::EnumSerializer, this class safely handles enum values,
+// validating that they are in range using the same EnumValidators as IPDL
+// (namely ContiguousEnumValidator and ContiguousEnumValidatorInclusive).
+template <typename E, typename EnumValidator>
+struct EnumSerializer {
+ typedef E ParamType;
+ typedef typename std::underlying_type<E>::type DataType;
+
+ template <typename U>
+ static QueueStatus Write(ProducerView<U>& aProducerView,
+ const ParamType& aValue) {
+ MOZ_RELEASE_ASSERT(EnumValidator::IsLegalValue(aValue));
+ return aProducerView.WriteParam(DataType(aValue));
+ }
+
+ template <typename U>
+ static QueueStatus Read(ConsumerView<U>& aConsumerView, ParamType* aResult) {
+ DataType value;
+ if (!aConsumerView.ReadParam(&value)) {
+ CrashReporter::AnnotateCrashReport(
+ CrashReporter::Annotation::IPCReadErrorReason, "Bad iter"_ns);
+ return aConsumerView.GetStatus();
+ }
+ if (!EnumValidator::IsLegalValue(ParamType(value))) {
+ CrashReporter::AnnotateCrashReport(
+ CrashReporter::Annotation::IPCReadErrorReason, "Illegal value"_ns);
+ return QueueStatus::kFatalError;
+ }
+
+ *aResult = ParamType(value);
+ return QueueStatus::kSuccess;
+ }
+};
+
+using IPC::ContiguousEnumValidator;
+using IPC::ContiguousEnumValidatorInclusive;
+
+template <typename E, E MinLegal, E HighBound>
+struct ContiguousEnumSerializer
+ : EnumSerializer<E, ContiguousEnumValidator<E, MinLegal, HighBound>> {};
+
+template <typename E, E MinLegal, E MaxLegal>
+struct ContiguousEnumSerializerInclusive
+ : EnumSerializer<E,
+ ContiguousEnumValidatorInclusive<E, MinLegal, MaxLegal>> {
+};
+
+// ---------------------------------------------------------------
+
+template <>
+struct QueueParamTraits<QueueStatus>
+ : public ContiguousEnumSerializerInclusive<
+ QueueStatus, QueueStatus::kSuccess, QueueStatus::kOOMError> {};
+
+// ---------------------------------------------------------------
+
+template <>
+struct QueueParamTraits<webgl::TexUnpackBlobDesc> {
+ using ParamType = webgl::TexUnpackBlobDesc;
+
+ template <typename U>
+ static QueueStatus Write(ProducerView<U>& view, const ParamType& in) {
+ MOZ_RELEASE_ASSERT(!in.image);
+ const bool isDataSurf = bool(in.dataSurf);
+ if (!view.WriteParam(in.imageTarget) || !view.WriteParam(in.size) ||
+ !view.WriteParam(in.srcAlphaType) || !view.WriteParam(in.unpacking) ||
+ !view.WriteParam(in.cpuData) || !view.WriteParam(in.pboOffset) ||
+ !view.WriteParam(in.imageSize) || !view.WriteParam(in.sd) ||
+ !view.WriteParam(isDataSurf)) {
+ return view.GetStatus();
+ }
+ if (isDataSurf) {
+ const auto& surf = in.dataSurf;
+ gfx::DataSourceSurface::ScopedMap map(surf, gfx::DataSourceSurface::READ);
+ if (!map.IsMapped()) {
+ return QueueStatus::kOOMError;
+ }
+ const auto& surfSize = surf->GetSize();
+ const auto stride = *MaybeAs<size_t>(map.GetStride());
+ if (!view.WriteParam(surfSize) || !view.WriteParam(surf->GetFormat()) ||
+ !view.WriteParam(stride)) {
+ return view.GetStatus();
+ }
+
+ const size_t dataSize = stride * surfSize.height;
+ const auto& begin = map.GetData();
+ if (!view.Write(begin, begin + dataSize)) {
+ return view.GetStatus();
+ }
+ }
+ return QueueStatus::kSuccess;
+ }
+
+ template <typename U>
+ static QueueStatus Read(ConsumerView<U>& view, ParamType* const out) {
+ bool isDataSurf;
+ if (!view.ReadParam(&out->imageTarget) || !view.ReadParam(&out->size) ||
+ !view.ReadParam(&out->srcAlphaType) ||
+ !view.ReadParam(&out->unpacking) || !view.ReadParam(&out->cpuData) ||
+ !view.ReadParam(&out->pboOffset) || !view.ReadParam(&out->imageSize) ||
+ !view.ReadParam(&out->sd) || !view.ReadParam(&isDataSurf)) {
+ return view.GetStatus();
+ }
+ if (isDataSurf) {
+ gfx::IntSize surfSize;
+ gfx::SurfaceFormat format;
+ size_t stride;
+ if (!view.ReadParam(&surfSize) || !view.ReadParam(&format) ||
+ !view.ReadParam(&stride)) {
+ return view.GetStatus();
+ }
+ auto& surf = out->dataSurf;
+ surf = gfx::Factory::CreateDataSourceSurfaceWithStride(surfSize, format,
+ stride, true);
+ if (!surf) {
+ return QueueStatus::kOOMError;
+ }
+
+ gfx::DataSourceSurface::ScopedMap map(surf,
+ gfx::DataSourceSurface::WRITE);
+ if (!map.IsMapped()) {
+ return QueueStatus::kOOMError;
+ }
+ const size_t dataSize = stride * surfSize.height;
+ const auto& begin = map.GetData();
+ if (!view.Read(begin, begin + dataSize)) {
+ return view.GetStatus();
+ }
+ }
+ return QueueStatus::kSuccess;
+ }
+};
+
+// ---------------------------------------------------------------
+
+template <>
+struct QueueParamTraits<nsACString> {
+ using ParamType = nsACString;
+
+ template <typename U>
+ static QueueStatus Write(ProducerView<U>& aProducerView,
+ const ParamType& aArg) {
+ if ((!aProducerView.WriteParam(aArg.IsVoid())) || aArg.IsVoid()) {
+ return aProducerView.GetStatus();
+ }
+
+ uint32_t len = aArg.Length();
+ if ((!aProducerView.WriteParam(len)) || (len == 0)) {
+ return aProducerView.GetStatus();
+ }
+
+ return aProducerView.Write(aArg.BeginReading(), len);
+ }
+
+ template <typename U>
+ static QueueStatus Read(ConsumerView<U>& aConsumerView, ParamType* aArg) {
+ bool isVoid = false;
+ if (!IsSuccess(aConsumerView.ReadParam(&isVoid))) {
+ return aConsumerView.GetStatus();
+ }
+ aArg->SetIsVoid(isVoid);
+ if (isVoid) {
+ return QueueStatus::kSuccess;
+ }
+
+ uint32_t len = 0;
+ if (!IsSuccess(aConsumerView.ReadParam(&len))) {
+ return aConsumerView.GetStatus();
+ }
+
+ if (len == 0) {
+ *aArg = "";
+ return QueueStatus::kSuccess;
+ }
+
+ char* buf = new char[len + 1];
+ if (!buf) {
+ return QueueStatus::kOOMError;
+ }
+ if (!IsSuccess(aConsumerView.Read(buf, len))) {
+ return aConsumerView.GetStatus();
+ }
+ buf[len] = '\0';
+ aArg->Adopt(buf, len);
+ return QueueStatus::kSuccess;
+ }
+};
+
+template <>
+struct QueueParamTraits<nsAString> {
+ using ParamType = nsAString;
+
+ template <typename U>
+ static QueueStatus Write(ProducerView<U>& aProducerView,
+ const ParamType& aArg) {
+ if ((!aProducerView.WriteParam(aArg.IsVoid())) || (aArg.IsVoid())) {
+ return aProducerView.GetStatus();
+ }
+ // DLP: No idea if this includes null terminator
+ uint32_t len = aArg.Length();
+ if ((!aProducerView.WriteParam(len)) || (len == 0)) {
+ return aProducerView.GetStatus();
+ }
+ constexpr const uint32_t sizeofchar = sizeof(typename ParamType::char_type);
+ return aProducerView.Write(aArg.BeginReading(), len * sizeofchar);
+ }
+
+ template <typename U>
+ static QueueStatus Read(ConsumerView<U>& aConsumerView, ParamType* aArg) {
+ bool isVoid = false;
+ if (!aConsumerView.ReadParam(&isVoid)) {
+ return aConsumerView.GetStatus();
+ }
+ aArg->SetIsVoid(isVoid);
+ if (isVoid) {
+ return QueueStatus::kSuccess;
+ }
+
+ // DLP: No idea if this includes null terminator
+ uint32_t len = 0;
+ if (!aConsumerView.ReadParam(&len)) {
+ return aConsumerView.GetStatus();
+ }
+
+ if (len == 0) {
+ *aArg = nsString();
+ return QueueStatus::kSuccess;
+ }
+
+ uint32_t sizeofchar = sizeof(typename ParamType::char_type);
+ typename ParamType::char_type* buf = nullptr;
+ buf = static_cast<typename ParamType::char_type*>(
+ malloc((len + 1) * sizeofchar));
+ if (!buf) {
+ return QueueStatus::kOOMError;
+ }
+
+ if (!aConsumerView.Read(buf, len * sizeofchar)) {
+ return aConsumerView.GetStatus();
+ }
+
+ buf[len] = L'\0';
+ aArg->Adopt(buf, len);
+ return QueueStatus::kSuccess;
+ }
+};
+
+template <>
+struct QueueParamTraits<nsCString> : public QueueParamTraits<nsACString> {
+ using ParamType = nsCString;
+};
+
+template <>
+struct QueueParamTraits<nsString> : public QueueParamTraits<nsAString> {
+ using ParamType = nsString;
+};
+
+// ---------------------------------------------------------------
+
+template <typename NSTArrayType,
+ bool =
+ IsTriviallySerializable<typename NSTArrayType::elem_type>::value>
+struct NSArrayQueueParamTraits;
+
+// For ElementTypes that are !IsTriviallySerializable
+template <typename _ElementType>
+struct NSArrayQueueParamTraits<nsTArray<_ElementType>, false> {
+ using ElementType = _ElementType;
+ using ParamType = nsTArray<ElementType>;
+
+ template <typename U>
+ static QueueStatus Write(ProducerView<U>& aProducerView,
+ const ParamType& aArg) {
+ aProducerView.WriteParam(aArg.Length());
+ for (auto& elt : aArg) {
+ aProducerView.WriteParam(elt);
+ }
+ return aProducerView.GetStatus();
+ }
+
+ template <typename U>
+ static QueueStatus Read(ConsumerView<U>& aConsumerView, ParamType* aArg) {
+ size_t arrayLen;
+ if (!aConsumerView.ReadParam(&arrayLen)) {
+ return aConsumerView.GetStatus();
+ }
+
+ if (!aArg->AppendElements(arrayLen, fallible)) {
+ return QueueStatus::kOOMError;
+ }
+
+ for (auto i : IntegerRange(arrayLen)) {
+ ElementType& elt = aArg->ElementAt(i);
+ aConsumerView.ReadParam(elt);
+ }
+ return aConsumerView.GetStatus();
+ }
+};
+
+// For ElementTypes that are IsTriviallySerializable
+template <typename _ElementType>
+struct NSArrayQueueParamTraits<nsTArray<_ElementType>, true> {
+ using ElementType = _ElementType;
+ using ParamType = nsTArray<ElementType>;
+
+ // TODO: Are there alignment issues?
+ template <typename U>
+ static QueueStatus Write(ProducerView<U>& aProducerView,
+ const ParamType& aArg) {
+ size_t arrayLen = aArg.Length();
+ aProducerView.WriteParam(arrayLen);
+ return aProducerView.Write(&aArg[0], aArg.Length() * sizeof(ElementType));
+ }
+
+ template <typename U>
+ static QueueStatus Read(ConsumerView<U>& aConsumerView, ParamType* aArg) {
+ size_t arrayLen;
+ if (!aConsumerView.ReadParam(&arrayLen)) {
+ return aConsumerView.GetStatus();
+ }
+
+ if (!aArg->AppendElements(arrayLen, fallible)) {
+ return QueueStatus::kOOMError;
+ }
+
+ return aConsumerView.Read(aArg->Elements(), arrayLen * sizeof(ElementType));
+ }
+};
+
+template <typename ElementType>
+struct QueueParamTraits<nsTArray<ElementType>>
+ : public NSArrayQueueParamTraits<nsTArray<ElementType>> {
+ using ParamType = nsTArray<ElementType>;
+};
+
+// ---------------------------------------------------------------
+
+template <typename ArrayType,
+ bool =
+ IsTriviallySerializable<typename ArrayType::ElementType>::value>
+struct ArrayQueueParamTraits;
+
+// For ElementTypes that are !IsTriviallySerializable
+template <typename _ElementType, size_t Length>
+struct ArrayQueueParamTraits<Array<_ElementType, Length>, false> {
+ using ElementType = _ElementType;
+ using ParamType = Array<ElementType, Length>;
+
+ template <typename U>
+ static QueueStatus Write(ProducerView<U>& aProducerView,
+ const ParamType& aArg) {
+ for (const auto& elt : aArg) {
+ aProducerView.WriteParam(elt);
+ }
+ return aProducerView.GetStatus();
+ }
+
+ template <typename U>
+ static QueueStatus Read(ConsumerView<U>& aConsumerView, ParamType* aArg) {
+ for (auto& elt : *aArg) {
+ aConsumerView.ReadParam(elt);
+ }
+ return aConsumerView.GetStatus();
+ }
+};
+
+// For ElementTypes that are IsTriviallySerializable
+template <typename _ElementType, size_t Length>
+struct ArrayQueueParamTraits<Array<_ElementType, Length>, true> {
+ using ElementType = _ElementType;
+ using ParamType = Array<ElementType, Length>;
+
+ template <typename U>
+ static QueueStatus Write(ProducerView<U>& aProducerView,
+ const ParamType& aArg) {
+ return aProducerView.Write(aArg.begin(), sizeof(ElementType[Length]));
+ }
+
+ template <typename U>
+ static QueueStatus Read(ConsumerView<U>& aConsumerView, ParamType* aArg) {
+ return aConsumerView.Read(aArg->begin(), sizeof(ElementType[Length]));
+ }
+};
+
+template <typename ElementType, size_t Length>
+struct QueueParamTraits<Array<ElementType, Length>>
+ : public ArrayQueueParamTraits<Array<ElementType, Length>> {
+ using ParamType = Array<ElementType, Length>;
+};
+
+// ---------------------------------------------------------------
+
+template <typename ElementType>
+struct QueueParamTraits<Maybe<ElementType>> {
+ using ParamType = Maybe<ElementType>;
+
+ template <typename U>
+ static QueueStatus Write(ProducerView<U>& aProducerView,
+ const ParamType& aArg) {
+ aProducerView.WriteParam(static_cast<bool>(aArg));
+ return aArg ? aProducerView.WriteParam(aArg.ref())
+ : aProducerView.GetStatus();
+ }
+
+ template <typename U>
+ static QueueStatus Read(ConsumerView<U>& aConsumerView, ParamType* aArg) {
+ bool isSome;
+ if (!aConsumerView.ReadParam(&isSome)) {
+ return aConsumerView.GetStatus();
+ }
+
+ if (!isSome) {
+ aArg->reset();
+ return QueueStatus::kSuccess;
+ }
+
+ aArg->emplace();
+ return aConsumerView.ReadParam(aArg->ptr());
+ }
+};
+
+// ---------------------------------------------------------------
+
+// Maybe<Variant> needs special behavior since Variant is not default
+// constructable. The Variant's first type must be default constructible.
+template <typename T, typename... Ts>
+struct QueueParamTraits<Maybe<Variant<T, Ts...>>> {
+ using ParamType = Maybe<Variant<T, Ts...>>;
+
+ template <typename U>
+ static QueueStatus Write(ProducerView<U>& aProducerView,
+ const ParamType& aArg) {
+ aProducerView.WriteParam(aArg.mIsSome);
+ return (aArg.mIsSome) ? aProducerView.WriteParam(aArg.ref())
+ : aProducerView.GetStatus();
+ }
+
+ template <typename U>
+ static QueueStatus Read(ConsumerView<U>& aConsumerView, ParamType* aArg) {
+ bool isSome;
+ if (!aConsumerView.ReadParam(&isSome)) {
+ return aConsumerView.GetStatus();
+ }
+
+ if (!isSome) {
+ aArg->reset();
+ return QueueStatus::kSuccess;
+ }
+
+ aArg->emplace(VariantType<T>());
+ return aConsumerView.ReadParam(aArg->ptr());
+ }
+};
+
+// ---------------------------------------------------------------
+
+template <typename TypeA, typename TypeB>
+struct QueueParamTraits<std::pair<TypeA, TypeB>> {
+ using ParamType = std::pair<TypeA, TypeB>;
+
+ template <typename U>
+ static QueueStatus Write(ProducerView<U>& aProducerView,
+ const ParamType& aArg) {
+ aProducerView.WriteParam(aArg.first());
+ return aProducerView.WriteParam(aArg.second());
+ }
+
+ template <typename U>
+ static QueueStatus Read(ConsumerView<U>& aConsumerView, ParamType* aArg) {
+ aConsumerView.ReadParam(aArg->first());
+ return aConsumerView.ReadParam(aArg->second());
+ }
+};
+
+// ---------------------------------------------------------------
+
+template <typename T>
+struct QueueParamTraits<UniquePtr<T>> {
+ using ParamType = UniquePtr<T>;
+
+ template <typename U>
+ static QueueStatus Write(ProducerView<U>& aProducerView,
+ const ParamType& aArg) {
+ // TODO: Clean up move with PCQ
+ aProducerView.WriteParam(!static_cast<bool>(aArg));
+ if (aArg && aProducerView.WriteParam(*aArg.get())) {
+ const_cast<ParamType&>(aArg).reset();
+ }
+ return aProducerView.GetStatus();
+ }
+
+ template <typename U>
+ static QueueStatus Read(ConsumerView<U>& aConsumerView, ParamType* aArg) {
+ bool isNull;
+ if (!aConsumerView.ReadParam(&isNull)) {
+ return aConsumerView.GetStatus();
+ }
+ if (isNull) {
+ aArg->reset(nullptr);
+ return QueueStatus::kSuccess;
+ }
+
+ T* obj = nullptr;
+ obj = new T();
+ if (!obj) {
+ return QueueStatus::kOOMError;
+ }
+ aArg->reset(obj);
+ return aConsumerView.ReadParam(obj);
+ }
+};
+
+// ---------------------------------------------------------------
+
+template <>
+struct QueueParamTraits<mozilla::ipc::Shmem> {
+ using ParamType = mozilla::ipc::Shmem;
+
+ template <typename U>
+ static QueueStatus Write(ProducerView<U>& aProducerView, ParamType&& aParam) {
+ if (!aProducerView.WriteParam(
+ aParam.Id(mozilla::ipc::Shmem::PrivateIPDLCaller()))) {
+ return aProducerView.GetStatus();
+ }
+
+ aParam.RevokeRights(mozilla::ipc::Shmem::PrivateIPDLCaller());
+ aParam.forget(mozilla::ipc::Shmem::PrivateIPDLCaller());
+ }
+
+ template <typename U>
+ static QueueStatus Read(ConsumerView<U>& aConsumerView, ParamType* aResult) {
+ ParamType::id_t id;
+ if (!aConsumerView.ReadParam(&id)) {
+ return aConsumerView.GetStatus();
+ }
+
+ mozilla::ipc::Shmem::SharedMemory* rawmem =
+ aConsumerView.LookupSharedMemory(id);
+ if (!rawmem) {
+ return QueueStatus::kFatalError;
+ }
+
+ *aResult = mozilla::ipc::Shmem(mozilla::ipc::Shmem::PrivateIPDLCaller(),
+ rawmem, id);
+ return QueueStatus::kSuccess;
+ }
+};
+
+} // namespace webgl
+} // namespace mozilla
+
+#endif // _QUEUEPARAMTRAITS_H_