From 26a029d407be480d791972afb5975cf62c9360a6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 02:47:55 +0200 Subject: Adding upstream version 124.0.1. Signed-off-by: Daniel Baumann --- dom/canvas/QueueParamTraits.h | 759 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 759 insertions(+) create mode 100644 dom/canvas/QueueParamTraits.h (limited to 'dom/canvas/QueueParamTraits.h') diff --git a/dom/canvas/QueueParamTraits.h b/dom/canvas/QueueParamTraits.h new file mode 100644 index 0000000000..fbedcdea0b --- /dev/null +++ b/dom/canvas/QueueParamTraits.h @@ -0,0 +1,759 @@ +/* -*- 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/Logging.h" +#include "mozilla/TimeStamp.h" +#include "nsExceptionHandler.h" +#include "nsString.h" +#include "WebGLTypes.h" + +#include + +namespace mozilla::webgl { + +template +struct RemoveCVR { + using Type = + typename std::remove_reference::type>::type; +}; + +/** + * 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::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 +struct QueueParamTraits; // Todo: s/QueueParamTraits/SizedParamTraits/ + +template +inline Range AsRange(T* const begin, T* const end) { + const auto size = MaybeAs(end - begin); + MOZ_RELEASE_ASSERT(size); + return {begin, *size}; +} + +// - +// BytesAlwaysValidT + +template +struct BytesAlwaysValidT { + using non_cv = typename std::remove_cv::type; + static constexpr bool value = + std::is_arithmetic::value && !std::is_same::value; +}; +static_assert(BytesAlwaysValidT::value); +static_assert(!BytesAlwaysValidT::value); +static_assert(!BytesAlwaysValidT::value); +static_assert(!BytesAlwaysValidT::value); +static_assert(BytesAlwaysValidT::value); + +template +struct BytesAlwaysValidT> { + static constexpr bool value = BytesAlwaysValidT::value; +}; +static_assert(BytesAlwaysValidT>::value); +static_assert(!BytesAlwaysValidT>::value); + +template +struct BytesAlwaysValidT { + static constexpr bool value = BytesAlwaysValidT::value; +}; +static_assert(BytesAlwaysValidT::value); +static_assert(!BytesAlwaysValidT::value); + +// - + +template <> +struct BytesAlwaysValidT { + static constexpr bool value = true; +}; + +// - + +/** + * 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 +class ProducerView { + public: + using Producer = _Producer; + + explicit ProducerView(Producer* aProducer) : mProducer(aProducer) {} + + template + bool WriteFromRange(const Range& src) { + static_assert(BytesAlwaysValidT::value); + if (MOZ_LIKELY(mOk)) { + mOk &= mProducer->WriteFromRange(src); + } + return mOk; + } + + /** + * Copy bytes from aBuffer to the producer if there is enough room. + * aBufferSize must not be 0. + */ + template + inline bool Write(const T* begin, const T* end) { + MOZ_RELEASE_ASSERT(begin <= end); + return WriteFromRange(AsRange(begin, end)); + } + + /** + * Serialize aArg using Arg's QueueParamTraits. + */ + template + bool WriteParam(const Arg& aArg) { + return mozilla::webgl::QueueParamTraits< + typename RemoveCVR::Type>::Write(*this, aArg); + } + + bool Ok() const { return mOk; } + + private: + Producer* const mProducer; + bool mOk = true; +}; + +/** + * Used to give QueueParamTraits a way to read from the Consumer without + * actually altering it, in case the transaction fails. + */ +template +class ConsumerView { + public: + using Consumer = _Consumer; + + explicit ConsumerView(Consumer* aConsumer) : mConsumer(aConsumer) {} + + /** + * Read bytes from the consumer if there is enough data. aBuffer may + * be null (in which case the data is skipped) + */ + template + inline bool Read(T* const destBegin, T* const destEnd) { + MOZ_ASSERT(destBegin); + MOZ_RELEASE_ASSERT(destBegin <= destEnd); + + const auto dest = AsRange(destBegin, destEnd); + const auto view = ReadRange(dest.length()); + if (MOZ_LIKELY(view)) { + const auto byteSize = ByteSize(dest); + if (MOZ_LIKELY(byteSize)) { + memcpy(dest.begin().get(), view->begin().get(), byteSize); + } + } + return mOk; + } + + /// Return a view wrapping the shmem. + template + inline Maybe> ReadRange(const size_t elemCount) { + static_assert(BytesAlwaysValidT::value); + if (MOZ_UNLIKELY(!mOk)) return {}; + const auto view = mConsumer->template ReadRange(elemCount); + mOk &= bool(view); + return view; + } + + /** + * Deserialize aArg using Arg's QueueParamTraits. + * If the return value is not Success then aArg is not changed. + */ + template + bool ReadParam(Arg* aArg) { + MOZ_ASSERT(aArg); + return mozilla::webgl::QueueParamTraits>::Read(*this, + aArg); + } + + bool Ok() const { return mOk; } + + private: + Consumer* const mConsumer; + bool mOk = true; +}; + +// - + +template +struct QueueParamTraits { + template + static bool Write(ProducerView& aProducerView, const Arg& aArg) { + static_assert(BytesAlwaysValidT::value, + "No QueueParamTraits specialization was found for this type " + "and it does not satisfy BytesAlwaysValid."); + // Write self as binary + const auto pArg = &aArg; + return aProducerView.Write(pArg, pArg + 1); + } + + template + static bool Read(ConsumerView& aConsumerView, Arg* aArg) { + static_assert(BytesAlwaysValidT::value, + "No QueueParamTraits specialization was found for this type " + "and it does not satisfy BytesAlwaysValid."); + // Read self as binary + return aConsumerView.Read(aArg, aArg + 1); + } +}; + +// --------------------------------------------------------------- + +template <> +struct QueueParamTraits { + using ParamType = bool; + + template + static auto Write(ProducerView& aProducerView, const ParamType& aArg) { + uint8_t temp = aArg ? 1 : 0; + return aProducerView.WriteParam(temp); + } + + template + static auto Read(ConsumerView& aConsumerView, ParamType* aArg) { + uint8_t temp; + if (aConsumerView.ReadParam(&temp)) { + MOZ_ASSERT(temp == 1 || temp == 0); + *aArg = temp ? true : false; + } + return aConsumerView.Ok(); + } +}; + +// --------------------------------------------------------------- + +template +Maybe AsValidEnum(const std::underlying_type_t raw_val) { + const auto raw_enum = T{raw_val}; // This is the risk we prevent! + if (!IsEnumCase(raw_enum)) return {}; + return Some(raw_enum); +} + +// - + +template +struct QueueParamTraits_IsEnumCase { + template + static bool Write(ProducerView& aProducerView, const T& aArg) { + MOZ_ASSERT(IsEnumCase(aArg)); + const auto shadow = static_cast>(aArg); + aProducerView.WriteParam(shadow); + return true; + } + + template + static bool Read(ConsumerView& aConsumerView, T* aArg) { + auto shadow = std::underlying_type_t{}; + aConsumerView.ReadParam(&shadow); + const auto e = AsValidEnum(shadow); + if (!e) return false; + *aArg = *e; + return true; + } +}; + +// --------------------------------------------------------------- + +// We guarantee our robustness via these requirements: +// * Object.MutTiedFields() gives us a tuple, +// * where the combined sizeofs all field types sums to sizeof(Object), +// * (thus we know we are exhaustively listing all fields) +// * where feeding each field back into ParamTraits succeeds, +// * and ParamTraits is only automated for BytesAlwaysValidT types. +// (BytesAlwaysValidT rejects bool and enum types, and only accepts int/float +// types, or array or std::arrays of such types) +// (Yes, bit-field fields are rejected by MutTiedFields too) + +template +struct QueueParamTraits_TiedFields { + template + static bool Write(ProducerView& aProducerView, const T& aArg) { + const auto fields = TiedFields(aArg); + static_assert(AreAllBytesTiedFields(), + "Are there missing fields or padding between fields?"); + + bool ok = true; + MapTuple(fields, [&](const auto& field) { + ok &= aProducerView.WriteParam(field); + return true; + }); + return ok; + } + + template + static bool Read(ConsumerView& aConsumerView, T* aArg) { + const auto fields = TiedFields(*aArg); + static_assert(AreAllBytesTiedFields()); + + bool ok = true; + MapTuple(fields, [&](auto& field) { + ok &= aConsumerView.ReadParam(&field); + return true; + }); + return ok; + } +}; + +// --------------------------------------------------------------- + +// 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 +struct EnumSerializer { + using ParamType = E; + using DataType = typename std::underlying_type::type; + + template + static auto Write(ProducerView& aProducerView, const ParamType& aValue) { + MOZ_RELEASE_ASSERT( + EnumValidator::IsLegalValue(static_cast(aValue))); + return aProducerView.WriteParam(DataType(aValue)); + } + + template + static bool Read(ConsumerView& aConsumerView, ParamType* aResult) { + DataType value; + if (!aConsumerView.ReadParam(&value)) { + CrashReporter::AnnotateCrashReport( + CrashReporter::Annotation::IPCReadErrorReason, "Bad iter"_ns); + return false; + } + if (!EnumValidator::IsLegalValue(static_cast(value))) { + CrashReporter::AnnotateCrashReport( + CrashReporter::Annotation::IPCReadErrorReason, "Illegal value"_ns); + return false; + } + + *aResult = ParamType(value); + return true; + } +}; + +using IPC::ContiguousEnumValidator; +using IPC::ContiguousEnumValidatorInclusive; + +template +struct ContiguousEnumSerializer + : EnumSerializer> {}; + +template +struct ContiguousEnumSerializerInclusive + : EnumSerializer> { +}; + +// --------------------------------------------------------------- + +template <> +struct QueueParamTraits { + using ParamType = webgl::TexUnpackBlobDesc; + + template + static bool Write(ProducerView& view, const ParamType& in) { + MOZ_RELEASE_ASSERT(!in.image); + MOZ_RELEASE_ASSERT(!in.sd); + 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.structuredSrcSize) || + !view.WriteParam(in.applyUnpackTransforms) || + !view.WriteParam(isDataSurf)) { + return false; + } + if (isDataSurf) { + const auto& surf = in.dataSurf; + gfx::DataSourceSurface::ScopedMap map(surf, gfx::DataSourceSurface::READ); + if (!map.IsMapped()) { + return false; + } + const auto& surfSize = surf->GetSize(); + const auto stride = *MaybeAs(map.GetStride()); + if (!view.WriteParam(surfSize) || !view.WriteParam(surf->GetFormat()) || + !view.WriteParam(stride)) { + return false; + } + + const size_t dataSize = stride * surfSize.height; + const auto& begin = map.GetData(); + const auto range = Range{begin, dataSize}; + if (!view.WriteFromRange(range)) { + return false; + } + } + return true; + } + + template + static bool Read(ConsumerView& 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->structuredSrcSize) || + !view.ReadParam(&out->applyUnpackTransforms) || + !view.ReadParam(&isDataSurf)) { + return false; + } + if (isDataSurf) { + gfx::IntSize surfSize; + gfx::SurfaceFormat format; + size_t stride; + if (!view.ReadParam(&surfSize) || !view.ReadParam(&format) || + !view.ReadParam(&stride)) { + return false; + } + const size_t dataSize = stride * surfSize.height; + const auto range = view.template ReadRange(dataSize); + if (!range) return false; + + // DataSourceSurface demands pointer-to-mutable. + const auto bytes = const_cast(range->begin().get()); + out->dataSurf = gfx::Factory::CreateWrappingDataSourceSurface( + bytes, stride, surfSize, format); + MOZ_ASSERT(out->dataSurf); + } + return true; + } +}; + +// --------------------------------------------------------------- + +template <> +struct QueueParamTraits { + using ParamType = nsACString; + + template + static bool Write(ProducerView& aProducerView, const ParamType& aArg) { + if ((!aProducerView.WriteParam(aArg.IsVoid())) || aArg.IsVoid()) { + return false; + } + + uint32_t len = aArg.Length(); + if ((!aProducerView.WriteParam(len)) || (len == 0)) { + return false; + } + + return aProducerView.Write(aArg.BeginReading(), len); + } + + template + static bool Read(ConsumerView& aConsumerView, ParamType* aArg) { + bool isVoid = false; + if (!aConsumerView.ReadParam(&isVoid)) { + return false; + } + aArg->SetIsVoid(isVoid); + if (isVoid) { + return true; + } + + uint32_t len = 0; + if (!aConsumerView.ReadParam(&len)) { + return false; + } + + if (len == 0) { + *aArg = ""; + return true; + } + + char* buf = new char[len + 1]; + if (!buf) { + return false; + } + if (!aConsumerView.Read(buf, len)) { + return false; + } + buf[len] = '\0'; + aArg->Adopt(buf, len); + return true; + } +}; + +template <> +struct QueueParamTraits { + using ParamType = nsAString; + + template + static bool Write(ProducerView& aProducerView, const ParamType& aArg) { + if ((!aProducerView.WriteParam(aArg.IsVoid())) || (aArg.IsVoid())) { + return false; + } + // DLP: No idea if this includes null terminator + uint32_t len = aArg.Length(); + if ((!aProducerView.WriteParam(len)) || (len == 0)) { + return false; + } + constexpr const uint32_t sizeofchar = sizeof(typename ParamType::char_type); + return aProducerView.Write(aArg.BeginReading(), len * sizeofchar); + } + + template + static bool Read(ConsumerView& aConsumerView, ParamType* aArg) { + bool isVoid = false; + if (!aConsumerView.ReadParam(&isVoid)) { + return false; + } + aArg->SetIsVoid(isVoid); + if (isVoid) { + return true; + } + + // DLP: No idea if this includes null terminator + uint32_t len = 0; + if (!aConsumerView.ReadParam(&len)) { + return false; + } + + if (len == 0) { + *aArg = nsString(); + return true; + } + + uint32_t sizeofchar = sizeof(typename ParamType::char_type); + typename ParamType::char_type* buf = nullptr; + buf = static_cast( + malloc((len + 1) * sizeofchar)); + if (!buf) { + return false; + } + + if (!aConsumerView.Read(buf, len * sizeofchar)) { + return false; + } + + buf[len] = L'\0'; + aArg->Adopt(buf, len); + return true; + } +}; + +template <> +struct QueueParamTraits : public QueueParamTraits { + using ParamType = nsCString; +}; + +template <> +struct QueueParamTraits : public QueueParamTraits { + using ParamType = nsString; +}; + +// --------------------------------------------------------------- + +template ::value> +struct NSArrayQueueParamTraits; + +// For ElementTypes that are !BytesAlwaysValidT +template +struct NSArrayQueueParamTraits, false> { + using ElementType = _ElementType; + using ParamType = nsTArray; + + template + static bool Write(ProducerView& aProducerView, const ParamType& aArg) { + aProducerView.WriteParam(aArg.Length()); + for (auto& elt : aArg) { + aProducerView.WriteParam(elt); + } + return true; + } + + template + static bool Read(ConsumerView& aConsumerView, ParamType* aArg) { + size_t arrayLen; + if (!aConsumerView.ReadParam(&arrayLen)) { + return false; + } + + if (!aArg->AppendElements(arrayLen, fallible)) { + return false; + } + + for (auto i : IntegerRange(arrayLen)) { + ElementType& elt = aArg->ElementAt(i); + aConsumerView.ReadParam(elt); + } + return aConsumerView.Ok(); + } +}; + +// For ElementTypes that are BytesAlwaysValidT +template +struct NSArrayQueueParamTraits, true> { + using ElementType = _ElementType; + using ParamType = nsTArray; + + // TODO: Are there alignment issues? + template + static bool Write(ProducerView& aProducerView, const ParamType& aArg) { + size_t arrayLen = aArg.Length(); + aProducerView.WriteParam(arrayLen); + return aProducerView.Write(&aArg[0], aArg.Length() * sizeof(ElementType)); + } + + template + static bool Read(ConsumerView& aConsumerView, ParamType* aArg) { + size_t arrayLen; + if (!aConsumerView.ReadParam(&arrayLen)) { + return false; + } + + if (!aArg->AppendElements(arrayLen, fallible)) { + return false; + } + + return aConsumerView.Read(aArg->Elements(), arrayLen * sizeof(ElementType)); + } +}; + +template +struct QueueParamTraits> + : public NSArrayQueueParamTraits> { + using ParamType = nsTArray; +}; + +// --------------------------------------------------------------- + +template ::value> +struct ArrayQueueParamTraits; + +// For ElementTypes that are !BytesAlwaysValidT +template +struct ArrayQueueParamTraits, false> { + using ElementType = _ElementType; + using ParamType = Array; + + template + static auto Write(ProducerView& aProducerView, const ParamType& aArg) { + for (const auto& elt : aArg) { + aProducerView.WriteParam(elt); + } + return aProducerView.Ok(); + } + + template + static auto Read(ConsumerView& aConsumerView, ParamType* aArg) { + for (auto& elt : *aArg) { + aConsumerView.ReadParam(elt); + } + return aConsumerView.Ok(); + } +}; + +// For ElementTypes that are BytesAlwaysValidT +template +struct ArrayQueueParamTraits, true> { + using ElementType = _ElementType; + using ParamType = Array; + + template + static auto Write(ProducerView& aProducerView, const ParamType& aArg) { + return aProducerView.Write(aArg.begin(), sizeof(ElementType[Length])); + } + + template + static auto Read(ConsumerView& aConsumerView, ParamType* aArg) { + return aConsumerView.Read(aArg->begin(), sizeof(ElementType[Length])); + } +}; + +template +struct QueueParamTraits> + : public ArrayQueueParamTraits> { + using ParamType = Array; +}; + +// --------------------------------------------------------------- + +template +struct QueueParamTraits> { + using ParamType = Maybe; + + template + static bool Write(ProducerView& aProducerView, const ParamType& aArg) { + aProducerView.WriteParam(static_cast(aArg)); + if (aArg) { + aProducerView.WriteParam(aArg.ref()); + } + return aProducerView.Ok(); + } + + template + static bool Read(ConsumerView& aConsumerView, ParamType* aArg) { + bool isSome; + if (!aConsumerView.ReadParam(&isSome)) { + return false; + } + + if (!isSome) { + aArg->reset(); + return true; + } + + aArg->emplace(); + return aConsumerView.ReadParam(aArg->ptr()); + } +}; + +// --------------------------------------------------------------- + +template +struct QueueParamTraits> { + using ParamType = std::pair; + + template + static bool Write(ProducerView& aProducerView, const ParamType& aArg) { + aProducerView.WriteParam(aArg.first()); + return aProducerView.WriteParam(aArg.second()); + } + + template + static bool Read(ConsumerView& aConsumerView, ParamType* aArg) { + aConsumerView.ReadParam(aArg->first()); + return aConsumerView.ReadParam(aArg->second()); + } +}; + +} // namespace mozilla::webgl + +#endif // _QUEUEPARAMTRAITS_H_ -- cgit v1.2.3