mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-23 21:01:08 +00:00
Bug 1690111 - Use new TypedArray APIs for processing data. r=farre,media-playback-reviewers,padenot,chunmin,sfink
Depends on D152497 Differential Revision: https://phabricator.services.mozilla.com/D152498
This commit is contained in:
parent
c49b5c3f78
commit
627ac90e0c
@ -117,29 +117,17 @@ void ChromeUtils::Base64URLEncode(GlobalObject& aGlobal,
|
||||
const ArrayBufferViewOrArrayBuffer& aSource,
|
||||
const Base64URLEncodeOptions& aOptions,
|
||||
nsACString& aResult, ErrorResult& aRv) {
|
||||
size_t length = 0;
|
||||
uint8_t* data = nullptr;
|
||||
if (aSource.IsArrayBuffer()) {
|
||||
const ArrayBuffer& buffer = aSource.GetAsArrayBuffer();
|
||||
buffer.ComputeState();
|
||||
length = buffer.Length();
|
||||
data = buffer.Data();
|
||||
} else if (aSource.IsArrayBufferView()) {
|
||||
const ArrayBufferView& view = aSource.GetAsArrayBufferView();
|
||||
view.ComputeState();
|
||||
length = view.Length();
|
||||
data = view.Data();
|
||||
} else {
|
||||
MOZ_CRASH("Uninitialized union: expected buffer or view");
|
||||
}
|
||||
|
||||
auto paddingPolicy = aOptions.mPad ? Base64URLEncodePaddingPolicy::Include
|
||||
: Base64URLEncodePaddingPolicy::Omit;
|
||||
nsresult rv = mozilla::Base64URLEncode(length, data, paddingPolicy, aResult);
|
||||
ProcessTypedArrays(
|
||||
aSource, [&](const Span<uint8_t>& aData, JS::AutoCheckCannotGC&&) {
|
||||
nsresult rv = mozilla::Base64URLEncode(aData.Length(), aData.Elements(),
|
||||
paddingPolicy, aResult);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
aResult.Truncate();
|
||||
aRv.Throw(rv);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* static */
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
#include "js/TypeDecls.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/dom/CompressionStreamBinding.h"
|
||||
#include "mozilla/dom/ReadableStream.h"
|
||||
#include "mozilla/dom/WritableStream.h"
|
||||
@ -57,16 +58,21 @@ class CompressionStreamAlgorithms : public TransformerAlgorithmsWrapper {
|
||||
// https://wicg.github.io/compression/#compress-and-enqueue-a-chunk
|
||||
|
||||
// Step 1: If chunk is not a BufferSource type, then throw a TypeError.
|
||||
// (ExtractSpanFromBufferSource does it)
|
||||
Span<const uint8_t> input = ExtractSpanFromBufferSource(cx, aChunk, aRv);
|
||||
if (aRv.Failed()) {
|
||||
RootedUnion<OwningArrayBufferViewOrArrayBuffer> bufferSource(cx);
|
||||
if (!bufferSource.Init(cx, aChunk)) {
|
||||
aRv.MightThrowJSException();
|
||||
aRv.StealExceptionFromJSContext(cx);
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 2: Let buffer be the result of compressing chunk with cs's format
|
||||
// and context.
|
||||
// Step 3 - 5: (Done in CompressAndEnqueue)
|
||||
CompressAndEnqueue(cx, input, ZLibFlush::No, aController, aRv);
|
||||
ProcessTypedArraysFixed(
|
||||
bufferSource,
|
||||
[&](const Span<uint8_t>& aData) MOZ_CAN_RUN_SCRIPT_BOUNDARY {
|
||||
CompressAndEnqueue(cx, aData, ZLibFlush::No, aController, aRv);
|
||||
});
|
||||
}
|
||||
|
||||
// Step 4 of
|
||||
|
@ -40,11 +40,9 @@ JSObject* Crypto::WrapObject(JSContext* aCx,
|
||||
void Crypto::GetRandomValues(JSContext* aCx, const ArrayBufferView& aArray,
|
||||
JS::MutableHandle<JSObject*> aRetval,
|
||||
ErrorResult& aRv) {
|
||||
JS::Rooted<JSObject*> view(aCx, aArray.Obj());
|
||||
|
||||
// Throw if the wrong type of ArrayBufferView is passed in
|
||||
// (Part of the Web Crypto API spec)
|
||||
switch (JS_GetArrayBufferViewType(view)) {
|
||||
switch (aArray.Type()) {
|
||||
case js::Scalar::Int8:
|
||||
case js::Scalar::Uint8:
|
||||
case js::Scalar::Uint8Clamped:
|
||||
@ -60,17 +58,6 @@ void Crypto::GetRandomValues(JSContext* aCx, const ArrayBufferView& aArray,
|
||||
return;
|
||||
}
|
||||
|
||||
aArray.ComputeState();
|
||||
uint32_t dataLen = aArray.Length();
|
||||
if (dataLen == 0) {
|
||||
NS_WARNING("ArrayBufferView length is 0, cannot continue");
|
||||
aRetval.set(view);
|
||||
return;
|
||||
} else if (dataLen > 65536) {
|
||||
aRv.Throw(NS_ERROR_DOM_QUOTA_EXCEEDED_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIRandomGenerator> randomGenerator =
|
||||
do_GetService("@mozilla.org/security/random-generator;1");
|
||||
if (!randomGenerator) {
|
||||
@ -78,14 +65,27 @@ void Crypto::GetRandomValues(JSContext* aCx, const ArrayBufferView& aArray,
|
||||
return;
|
||||
}
|
||||
|
||||
nsresult rv =
|
||||
randomGenerator->GenerateRandomBytesInto(aArray.Data(), dataLen);
|
||||
aArray.ProcessFixedData([&](const Span<uint8_t>& aData) {
|
||||
if (aData.Length() == 0) {
|
||||
NS_WARNING("ArrayBufferView length is 0, cannot continue");
|
||||
aRetval.set(aArray.Obj());
|
||||
return;
|
||||
}
|
||||
|
||||
if (aData.Length() > 65536) {
|
||||
aRv.Throw(NS_ERROR_DOM_QUOTA_EXCEEDED_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
nsresult rv = randomGenerator->GenerateRandomBytesInto(aData.Elements(),
|
||||
aData.Length());
|
||||
if (NS_FAILED(rv)) {
|
||||
aRv.Throw(NS_ERROR_DOM_OPERATION_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
aRetval.set(view);
|
||||
aRetval.set(aArray.Obj());
|
||||
});
|
||||
}
|
||||
|
||||
void Crypto::RandomUUID(nsACString& aRetVal) {
|
||||
|
@ -199,29 +199,33 @@ already_AddRefed<DOMMatrixReadOnly> DOMMatrixReadOnly::FromMatrix(
|
||||
already_AddRefed<DOMMatrixReadOnly> DOMMatrixReadOnly::FromFloat32Array(
|
||||
const GlobalObject& aGlobal, const Float32Array& aArray32,
|
||||
ErrorResult& aRv) {
|
||||
aArray32.ComputeState();
|
||||
|
||||
const int length = aArray32.Length();
|
||||
nsCOMPtr<nsISupports> global = aGlobal.GetAsSupports();
|
||||
return aArray32.ProcessData(
|
||||
[&](const Span<float>& aData, JS::AutoCheckCannotGC&& nogc) {
|
||||
const int length = aData.Length();
|
||||
const bool is2D = length == 6;
|
||||
RefPtr<DOMMatrixReadOnly> obj =
|
||||
new DOMMatrixReadOnly(aGlobal.GetAsSupports(), is2D);
|
||||
SetDataInMatrix(obj, aArray32.Data(), length, aRv);
|
||||
|
||||
new DOMMatrixReadOnly(global.forget(), is2D);
|
||||
SetDataInMatrix(obj, aData.Elements(), length, aRv);
|
||||
nogc.reset(); // Done with aData
|
||||
return obj.forget();
|
||||
});
|
||||
}
|
||||
|
||||
already_AddRefed<DOMMatrixReadOnly> DOMMatrixReadOnly::FromFloat64Array(
|
||||
const GlobalObject& aGlobal, const Float64Array& aArray64,
|
||||
ErrorResult& aRv) {
|
||||
aArray64.ComputeState();
|
||||
|
||||
const int length = aArray64.Length();
|
||||
nsCOMPtr<nsISupports> global = aGlobal.GetAsSupports();
|
||||
return aArray64.ProcessData(
|
||||
[&](const Span<double>& aData, JS::AutoCheckCannotGC&& nogc) {
|
||||
const int length = aData.Length();
|
||||
const bool is2D = length == 6;
|
||||
RefPtr<DOMMatrixReadOnly> obj =
|
||||
new DOMMatrixReadOnly(aGlobal.GetAsSupports(), is2D);
|
||||
SetDataInMatrix(obj, aArray64.Data(), length, aRv);
|
||||
|
||||
new DOMMatrixReadOnly(global.forget(), is2D);
|
||||
SetDataInMatrix(obj, aData.Elements(), length, aRv);
|
||||
nogc.reset(); // Done with aData
|
||||
return obj.forget();
|
||||
});
|
||||
}
|
||||
|
||||
already_AddRefed<DOMMatrixReadOnly> DOMMatrixReadOnly::Constructor(
|
||||
@ -642,27 +646,31 @@ already_AddRefed<DOMMatrix> DOMMatrix::FromMatrix(
|
||||
already_AddRefed<DOMMatrix> DOMMatrix::FromFloat32Array(
|
||||
const GlobalObject& aGlobal, const Float32Array& aArray32,
|
||||
ErrorResult& aRv) {
|
||||
aArray32.ComputeState();
|
||||
|
||||
const int length = aArray32.Length();
|
||||
nsCOMPtr<nsISupports> global = aGlobal.GetAsSupports();
|
||||
return aArray32.ProcessData(
|
||||
[&](const Span<float>& aData, JS::AutoCheckCannotGC&& nogc) {
|
||||
const int length = aData.Length();
|
||||
const bool is2D = length == 6;
|
||||
RefPtr<DOMMatrix> obj = new DOMMatrix(aGlobal.GetAsSupports(), is2D);
|
||||
SetDataInMatrix(obj, aArray32.Data(), length, aRv);
|
||||
|
||||
RefPtr<DOMMatrix> obj = new DOMMatrix(global.forget(), is2D);
|
||||
SetDataInMatrix(obj, aData.Elements(), length, aRv);
|
||||
nogc.reset(); // Done with aData
|
||||
return obj.forget();
|
||||
});
|
||||
}
|
||||
|
||||
already_AddRefed<DOMMatrix> DOMMatrix::FromFloat64Array(
|
||||
const GlobalObject& aGlobal, const Float64Array& aArray64,
|
||||
ErrorResult& aRv) {
|
||||
aArray64.ComputeState();
|
||||
|
||||
const int length = aArray64.Length();
|
||||
nsCOMPtr<nsISupports> global = aGlobal.GetAsSupports();
|
||||
return aArray64.ProcessData(
|
||||
[&](const Span<double>& aData, JS::AutoCheckCannotGC&& nogc) {
|
||||
const int length = aData.Length();
|
||||
const bool is2D = length == 6;
|
||||
RefPtr<DOMMatrix> obj = new DOMMatrix(aGlobal.GetAsSupports(), is2D);
|
||||
SetDataInMatrix(obj, aArray64.Data(), length, aRv);
|
||||
|
||||
RefPtr<DOMMatrix> obj = new DOMMatrix(global.forget(), is2D);
|
||||
SetDataInMatrix(obj, aData.Elements(), length, aRv);
|
||||
nogc.reset(); // Done with aData
|
||||
return obj.forget();
|
||||
});
|
||||
}
|
||||
|
||||
already_AddRefed<DOMMatrix> DOMMatrix::Constructor(
|
||||
|
@ -254,7 +254,10 @@ class DOMMatrixReadOnly : public nsWrapperCache {
|
||||
DOMMatrixReadOnly* SetMatrixValue(const nsACString&, ErrorResult&);
|
||||
void Ensure3DMatrix();
|
||||
|
||||
DOMMatrixReadOnly(nsISupports* aParent, bool is2D) : mParent(aParent) {
|
||||
DOMMatrixReadOnly(nsISupports* aParent, bool is2D)
|
||||
: DOMMatrixReadOnly(do_AddRef(aParent), is2D) {}
|
||||
DOMMatrixReadOnly(already_AddRefed<nsISupports>&& aParent, bool is2D)
|
||||
: mParent(std::move(aParent)) {
|
||||
if (is2D) {
|
||||
mMatrix2D = MakeUnique<gfx::MatrixDouble>();
|
||||
} else {
|
||||
@ -335,6 +338,8 @@ class DOMMatrix : public DOMMatrixReadOnly {
|
||||
private:
|
||||
DOMMatrix(nsISupports* aParent, bool is2D)
|
||||
: DOMMatrixReadOnly(aParent, is2D) {}
|
||||
DOMMatrix(already_AddRefed<nsISupports>&& aParent, bool is2D)
|
||||
: DOMMatrixReadOnly(std::move(aParent), is2D) {}
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
@ -120,8 +120,9 @@ already_AddRefed<Document> DOMParser::ParseFromSafeString(const nsAString& aStr,
|
||||
already_AddRefed<Document> DOMParser::ParseFromBuffer(const Uint8Array& aBuf,
|
||||
SupportedType aType,
|
||||
ErrorResult& aRv) {
|
||||
aBuf.ComputeState();
|
||||
return ParseFromBuffer(Span(aBuf.Data(), aBuf.Length()), aType, aRv);
|
||||
return aBuf.ProcessFixedData([&](const Span<uint8_t>& aData) {
|
||||
return ParseFromBuffer(aData, aType, aRv);
|
||||
});
|
||||
}
|
||||
|
||||
already_AddRefed<Document> DOMParser::ParseFromBuffer(Span<const uint8_t> aBuf,
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "mozilla/dom/TextDecoderStream.h"
|
||||
#include "mozilla/dom/TransformStream.h"
|
||||
#include "mozilla/dom/TransformerCallbackHelpers.h"
|
||||
#include "mozilla/dom/UnionTypes.h"
|
||||
|
||||
#include "ZLibHelper.h"
|
||||
|
||||
@ -54,16 +55,21 @@ class DecompressionStreamAlgorithms : public TransformerAlgorithmsWrapper {
|
||||
// https://wicg.github.io/compression/#compress-and-enqueue-a-chunk
|
||||
|
||||
// Step 1: If chunk is not a BufferSource type, then throw a TypeError.
|
||||
// (ExtractSpanFromBufferSource does it)
|
||||
Span<const uint8_t> input = ExtractSpanFromBufferSource(cx, aChunk, aRv);
|
||||
if (aRv.Failed()) {
|
||||
RootedUnion<OwningArrayBufferViewOrArrayBuffer> bufferSource(cx);
|
||||
if (!bufferSource.Init(cx, aChunk)) {
|
||||
aRv.MightThrowJSException();
|
||||
aRv.StealExceptionFromJSContext(cx);
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 2: Let buffer be the result of decompressing chunk with ds's format
|
||||
// and context. If this results in an error, then throw a TypeError.
|
||||
// Step 3 - 5: (Done in CompressAndEnqueue)
|
||||
DecompressAndEnqueue(cx, input, ZLibFlush::No, aController, aRv);
|
||||
ProcessTypedArraysFixed(
|
||||
bufferSource,
|
||||
[&](const Span<uint8_t>& aData) MOZ_CAN_RUN_SCRIPT_BOUNDARY {
|
||||
DecompressAndEnqueue(cx, aData, ZLibFlush::No, aController, aRv);
|
||||
});
|
||||
}
|
||||
|
||||
// Step 4 of
|
||||
|
@ -415,18 +415,6 @@ struct TypedArray_base : public SpiderMonkeyInterfaceObjectStorage,
|
||||
return mData;
|
||||
}
|
||||
|
||||
// Return a pointer to data that will not move during a GC.
|
||||
//
|
||||
// For some smaller views, this will copy the data into the provided buffer
|
||||
// and return that buffer as the pointer. Otherwise, this will return a
|
||||
// direct pointer to the actual data with no copying. If the provided buffer
|
||||
// is not large enough, nullptr will be returned. If bufSize is at least
|
||||
// JS_MaxMovableTypedArraySize(), the data is guaranteed to fit.
|
||||
inline element_type* FixedData(uint8_t* buffer, size_t bufSize) const {
|
||||
MOZ_ASSERT(mComputed);
|
||||
return JS_GetArrayBufferViewFixedData(mImplObj, buffer, bufSize);
|
||||
}
|
||||
|
||||
inline uint32_t Length() const {
|
||||
MOZ_ASSERT(mComputed);
|
||||
return mLength;
|
||||
@ -714,6 +702,7 @@ struct TypedArray_base : public SpiderMonkeyInterfaceObjectStorage,
|
||||
return CallProcessor(GetCurrentData(), std::forward<Processor>(aProcessor));
|
||||
}
|
||||
|
||||
public:
|
||||
inline void Reset() {
|
||||
// This method mostly exists to inform the GC rooting hazard analysis that
|
||||
// the variable can be considered dead, at least until you do anything else
|
||||
|
@ -5907,8 +5907,7 @@ nsresult CanvasRenderingContext2D::GetImageDataArray(
|
||||
nsIPrincipal& aSubjectPrincipal, JSObject** aRetval) {
|
||||
MOZ_ASSERT(aWidth && aHeight);
|
||||
|
||||
// Restrict the typed array length to INT32_MAX because that's all we support
|
||||
// in dom::TypedArray::ComputeState.
|
||||
// Restrict the typed array length to INT32_MAX because that's all we support.
|
||||
CheckedInt<uint32_t> len = CheckedInt<uint32_t>(aWidth) * aHeight * 4;
|
||||
if (!len.isValid() || len.value() > INT32_MAX) {
|
||||
return NS_ERROR_DOM_INDEX_SIZE_ERR;
|
||||
@ -6137,18 +6136,13 @@ void CanvasRenderingContext2D::PutImageData_explicit(
|
||||
return;
|
||||
}
|
||||
|
||||
arr.ComputeState();
|
||||
RefPtr<DataSourceSurface> sourceSurface;
|
||||
uint8_t* lockedBits = nullptr;
|
||||
|
||||
uint32_t dataLen = arr.Length();
|
||||
|
||||
uint32_t len = width * height * 4;
|
||||
if (dataLen != len) {
|
||||
return aRv.ThrowInvalidStateError("Invalid width or height");
|
||||
}
|
||||
|
||||
// The canvas spec says that the current path, transformation matrix, shadow
|
||||
// attributes, global alpha, the clipping region, and global composition
|
||||
// operator must not affect the getImageData() and putImageData() methods.
|
||||
// The canvas spec says that the current path, transformation matrix,
|
||||
// shadow attributes, global alpha, the clipping region, and global
|
||||
// composition operator must not affect the getImageData() and
|
||||
// putImageData() methods.
|
||||
const gfx::Rect putRect(dirtyRect);
|
||||
EnsureTarget(&putRect);
|
||||
|
||||
@ -6157,8 +6151,6 @@ void CanvasRenderingContext2D::PutImageData_explicit(
|
||||
}
|
||||
|
||||
DataSourceSurface::MappedSurface map;
|
||||
RefPtr<DataSourceSurface> sourceSurface;
|
||||
uint8_t* lockedBits = nullptr;
|
||||
uint8_t* dstData;
|
||||
IntSize dstSize;
|
||||
int32_t dstStride;
|
||||
@ -6169,10 +6161,11 @@ void CanvasRenderingContext2D::PutImageData_explicit(
|
||||
sourceSurface = Factory::CreateDataSourceSurface(
|
||||
dirtyRect.Size(), SurfaceFormat::B8G8R8A8, false);
|
||||
|
||||
// In certain scenarios, requesting larger than 8k image fails. Bug 803568
|
||||
// covers the details of how to run into it, but the full detailed
|
||||
// investigation hasn't been done to determine the underlying cause. We
|
||||
// will just handle the failure to allocate the surface to avoid a crash.
|
||||
// In certain scenarios, requesting larger than 8k image fails. Bug
|
||||
// 803568 covers the details of how to run into it, but the full
|
||||
// detailed investigation hasn't been done to determine the
|
||||
// underlying cause. We will just handle the failure to allocate
|
||||
// the surface to avoid a crash.
|
||||
if (!sourceSurface) {
|
||||
return aRv.Throw(NS_ERROR_FAILURE);
|
||||
}
|
||||
@ -6188,12 +6181,27 @@ void CanvasRenderingContext2D::PutImageData_explicit(
|
||||
dstFormat = sourceSurface->GetFormat();
|
||||
}
|
||||
|
||||
uint8_t* srcData = arr.Data() + srcRect.y * (width * 4) + srcRect.x * 4;
|
||||
arr.ProcessData(
|
||||
[&](const Span<uint8_t>& aData, JS::AutoCheckCannotGC&& nogc) {
|
||||
// Verify that the length hasn't changed.
|
||||
if (aData.Length() != width * height * 4) {
|
||||
// FIXME Should this call ReleaseBits/Unmap?
|
||||
return aRv.ThrowInvalidStateError("Invalid width or height");
|
||||
}
|
||||
|
||||
PremultiplyData(
|
||||
srcData, width * 4, SurfaceFormat::R8G8B8A8, dstData, dstStride,
|
||||
mOpaque ? SurfaceFormat::X8R8G8B8_UINT32 : SurfaceFormat::A8R8G8B8_UINT32,
|
||||
uint8_t* srcData =
|
||||
aData.Elements() + srcRect.y * (width * 4) + srcRect.x * 4;
|
||||
|
||||
PremultiplyData(srcData, width * 4, SurfaceFormat::R8G8B8A8, dstData,
|
||||
dstStride,
|
||||
mOpaque ? SurfaceFormat::X8R8G8B8_UINT32
|
||||
: SurfaceFormat::A8R8G8B8_UINT32,
|
||||
dirtyRect.Size());
|
||||
});
|
||||
|
||||
if (aRv.Failed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (lockedBits) {
|
||||
mTarget->ReleaseBits(lockedBits);
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "js/ScalarType.h" // js::Scalar::Type
|
||||
#include "mozilla/dom/Document.h"
|
||||
#include "mozilla/dom/ToJSValue.h"
|
||||
#include "mozilla/dom/TypedArray.h"
|
||||
#include "mozilla/dom/WebGLContextEvent.h"
|
||||
#include "mozilla/dom/WorkerCommon.h"
|
||||
#include "mozilla/EnumeratedRange.h"
|
||||
@ -434,6 +435,44 @@ void ClientWebGLContext::Run(Args&&... args) const {
|
||||
webgl::Serialize(destBytes, id, args...);
|
||||
}
|
||||
|
||||
template <typename MethodType, MethodType method, typename... Args>
|
||||
void ClientWebGLContext::RunWithGCData(JS::AutoCheckCannotGC&& aNoGC,
|
||||
Args&&... args) const {
|
||||
// Hold a strong-ref to prevent LoseContext=>UAF.
|
||||
//
|
||||
// Note that `aNoGC` must be reset after the GC data is done being used and
|
||||
// before the `notLost` destructor runs, since it could GC.
|
||||
const auto notLost = mNotLost;
|
||||
if (IsContextLost()) {
|
||||
aNoGC.reset(); // GC data will not be used.
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& inProcess = notLost->inProcess;
|
||||
if (inProcess) {
|
||||
(inProcess.get()->*method)(std::forward<Args>(args)...);
|
||||
aNoGC.reset(); // Done with any GC data
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& child = notLost->outOfProcess;
|
||||
|
||||
const auto id = IdByMethod<MethodType, method>();
|
||||
|
||||
const auto info = webgl::SerializationInfo(id, args...);
|
||||
const auto maybeDest = child->AllocPendingCmdBytes(info.requiredByteCount,
|
||||
info.alignmentOverhead);
|
||||
if (!maybeDest) {
|
||||
aNoGC.reset(); // GC data will not be used.
|
||||
JsWarning("Failed to allocate internal command buffer.");
|
||||
OnContextLoss(webgl::ContextLossReason::None);
|
||||
return;
|
||||
}
|
||||
const auto& destBytes = *maybeDest;
|
||||
webgl::Serialize(destBytes, id, args...);
|
||||
aNoGC.reset(); // Done with any GC data
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Client-side helper methods. Dispatch to a Host method.
|
||||
// -------------------------------------------------------------------------
|
||||
@ -2873,13 +2912,14 @@ void ClientWebGLContext::Clear(GLbitfield mask) {
|
||||
void ClientWebGLContext::ClearBufferTv(const GLenum buffer,
|
||||
const GLint drawBuffer,
|
||||
const webgl::AttribBaseType type,
|
||||
const Range<const uint8_t>& view,
|
||||
JS::AutoCheckCannotGC&& nogc,
|
||||
const Span<const uint8_t>& view,
|
||||
const GLuint srcElemOffset) {
|
||||
const FuncScope funcScope(*this, "clearBufferu?[fi]v");
|
||||
if (IsContextLost()) return;
|
||||
|
||||
const auto byteOffset = CheckedInt<size_t>(srcElemOffset) * sizeof(float);
|
||||
if (!byteOffset.isValid() || byteOffset.value() > view.length()) {
|
||||
if (!byteOffset.isValid() || byteOffset.value() > view.Length()) {
|
||||
nogc.reset();
|
||||
EnqueueError(LOCAL_GL_INVALID_VALUE, "`srcOffset` too large for `values`.");
|
||||
return;
|
||||
}
|
||||
@ -2900,17 +2940,20 @@ void ClientWebGLContext::ClearBufferTv(const GLenum buffer,
|
||||
break;
|
||||
|
||||
default:
|
||||
nogc.reset();
|
||||
EnqueueError_ArgEnum("buffer", buffer);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto requiredBytes = byteOffset + dataSize;
|
||||
if (!requiredBytes.isValid() || requiredBytes.value() > view.length()) {
|
||||
if (!requiredBytes.isValid() || requiredBytes.value() > view.Length()) {
|
||||
nogc.reset();
|
||||
EnqueueError(LOCAL_GL_INVALID_VALUE, "`values` too small.");
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(data.data.data(), view.begin().get() + byteOffset.value(), dataSize);
|
||||
memcpy(data.data.data(), view.data() + byteOffset.value(), dataSize);
|
||||
nogc.reset(); // Done with `view`.
|
||||
Run<RPROC(ClearBufferTv)>(buffer, drawBuffer, data);
|
||||
|
||||
AfterDrawCall();
|
||||
@ -3356,6 +3399,14 @@ void ClientWebGLContext::BindBufferRangeImpl(const GLenum target,
|
||||
size);
|
||||
}
|
||||
|
||||
static inline size_t SizeOfViewElem(const dom::ArrayBufferView& view) {
|
||||
const auto& elemType = view.Type();
|
||||
if (elemType == js::Scalar::MaxTypedArrayViewType) // DataViews.
|
||||
return 1;
|
||||
|
||||
return js::Scalar::byteSize(elemType);
|
||||
}
|
||||
|
||||
void ClientWebGLContext::GetBufferSubData(GLenum target, GLintptr srcByteOffset,
|
||||
const dom::ArrayBufferView& dstData,
|
||||
GLuint dstElemOffset,
|
||||
@ -3366,24 +3417,25 @@ void ClientWebGLContext::GetBufferSubData(GLenum target, GLintptr srcByteOffset,
|
||||
mNotLost; // Hold a strong-ref to prevent LoseContext=>UAF.
|
||||
if (!ValidateNonNegative("srcByteOffset", srcByteOffset)) return;
|
||||
|
||||
uint8_t* bytes;
|
||||
size_t byteLen;
|
||||
if (!ValidateArrayBufferView(dstData, dstElemOffset, dstElemCountOverride,
|
||||
LOCAL_GL_INVALID_VALUE, &bytes, &byteLen)) {
|
||||
size_t elemSize = SizeOfViewElem(dstData);
|
||||
dstData.ProcessFixedData([&](const Span<uint8_t>& aData) {
|
||||
const auto& destView =
|
||||
ValidateArrayBufferView(aData, elemSize, dstElemOffset,
|
||||
dstElemCountOverride, LOCAL_GL_INVALID_VALUE);
|
||||
if (!destView) {
|
||||
return;
|
||||
}
|
||||
const auto destView = Range<uint8_t>{bytes, byteLen};
|
||||
|
||||
const auto& inProcessContext = notLost->inProcess;
|
||||
if (inProcessContext) {
|
||||
inProcessContext->GetBufferSubData(target, srcByteOffset, destView);
|
||||
inProcessContext->GetBufferSubData(target, srcByteOffset, *destView);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& child = notLost->outOfProcess;
|
||||
child->FlushPendingCmds();
|
||||
mozilla::ipc::Shmem rawShmem;
|
||||
if (!child->SendGetBufferSubData(target, srcByteOffset, destView.length(),
|
||||
if (!child->SendGetBufferSubData(target, srcByteOffset, destView->length(),
|
||||
&rawShmem)) {
|
||||
return;
|
||||
}
|
||||
@ -3394,14 +3446,15 @@ void ClientWebGLContext::GetBufferSubData(GLenum target, GLintptr srcByteOffset,
|
||||
}
|
||||
|
||||
const auto shmemView = shmem.ByteRange();
|
||||
MOZ_RELEASE_ASSERT(shmemView.length() == 1 + destView.length());
|
||||
MOZ_RELEASE_ASSERT(shmemView.length() == 1 + destView->length());
|
||||
|
||||
const auto ok = bool(*(shmemView.begin().get()));
|
||||
const auto srcView =
|
||||
Range<const uint8_t>{shmemView.begin() + 1, shmemView.end()};
|
||||
if (ok) {
|
||||
Memcpy(destView.begin(), srcView.begin(), srcView.length());
|
||||
Memcpy(destView->begin(), srcView.begin(), srcView.length());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
////
|
||||
@ -3428,9 +3481,9 @@ void ClientWebGLContext::BufferData(
|
||||
if (!ValidateNonNull("src", maybeSrc)) return;
|
||||
const auto& src = maybeSrc.Value();
|
||||
|
||||
src.ComputeState();
|
||||
const auto range = Range<const uint8_t>{src.Data(), src.Length()};
|
||||
Run<RPROC(BufferData)>(target, RawBuffer<>(range), usage);
|
||||
src.ProcessFixedData([&](const Span<uint8_t>& aData) {
|
||||
Run<RPROC(BufferData)>(target, RawBuffer<>(aData), usage);
|
||||
});
|
||||
}
|
||||
|
||||
void ClientWebGLContext::BufferData(GLenum target,
|
||||
@ -3438,14 +3491,16 @@ void ClientWebGLContext::BufferData(GLenum target,
|
||||
GLenum usage, GLuint srcElemOffset,
|
||||
GLuint srcElemCountOverride) {
|
||||
const FuncScope funcScope(*this, "bufferData");
|
||||
uint8_t* bytes;
|
||||
size_t byteLen;
|
||||
if (!ValidateArrayBufferView(src, srcElemOffset, srcElemCountOverride,
|
||||
LOCAL_GL_INVALID_VALUE, &bytes, &byteLen)) {
|
||||
size_t elemSize = SizeOfViewElem(src);
|
||||
src.ProcessFixedData([&](const Span<uint8_t>& aData) {
|
||||
const auto& range =
|
||||
ValidateArrayBufferView(aData, elemSize, srcElemOffset,
|
||||
srcElemCountOverride, LOCAL_GL_INVALID_VALUE);
|
||||
if (!range) {
|
||||
return;
|
||||
}
|
||||
const auto range = Range<const uint8_t>{bytes, byteLen};
|
||||
Run<RPROC(BufferData)>(target, RawBuffer<>(range), usage);
|
||||
Run<RPROC(BufferData)>(target, RawBuffer<>(*range), usage);
|
||||
});
|
||||
}
|
||||
|
||||
void ClientWebGLContext::RawBufferData(GLenum target, const uint8_t* srcBytes,
|
||||
@ -3473,10 +3528,10 @@ void ClientWebGLContext::BufferSubData(GLenum target,
|
||||
WebGLsizeiptr dstByteOffset,
|
||||
const dom::ArrayBuffer& src) {
|
||||
const FuncScope funcScope(*this, "bufferSubData");
|
||||
src.ComputeState();
|
||||
const auto range = Range<const uint8_t>{src.Data(), src.Length()};
|
||||
Run<RPROC(BufferSubData)>(target, dstByteOffset, RawBuffer<>(range),
|
||||
src.ProcessFixedData([&](const Span<uint8_t>& aData) {
|
||||
Run<RPROC(BufferSubData)>(target, dstByteOffset, RawBuffer<>(aData),
|
||||
/* unsynchronized */ false);
|
||||
});
|
||||
}
|
||||
|
||||
void ClientWebGLContext::BufferSubData(GLenum target,
|
||||
@ -3485,15 +3540,17 @@ void ClientWebGLContext::BufferSubData(GLenum target,
|
||||
GLuint srcElemOffset,
|
||||
GLuint srcElemCountOverride) {
|
||||
const FuncScope funcScope(*this, "bufferSubData");
|
||||
uint8_t* bytes;
|
||||
size_t byteLen;
|
||||
if (!ValidateArrayBufferView(src, srcElemOffset, srcElemCountOverride,
|
||||
LOCAL_GL_INVALID_VALUE, &bytes, &byteLen)) {
|
||||
size_t elemSize = SizeOfViewElem(src);
|
||||
src.ProcessFixedData([&](const Span<uint8_t>& aData) {
|
||||
const auto& range =
|
||||
ValidateArrayBufferView(aData, elemSize, srcElemOffset,
|
||||
srcElemCountOverride, LOCAL_GL_INVALID_VALUE);
|
||||
if (!range) {
|
||||
return;
|
||||
}
|
||||
const auto range = Range<const uint8_t>{bytes, byteLen};
|
||||
Run<RPROC(BufferSubData)>(target, dstByteOffset, RawBuffer<>(range),
|
||||
Run<RPROC(BufferSubData)>(target, dstByteOffset, RawBuffer<>(*range),
|
||||
/* unsynchronized */ false);
|
||||
});
|
||||
}
|
||||
|
||||
void ClientWebGLContext::CopyBufferSubData(GLenum readTarget,
|
||||
@ -4082,19 +4139,11 @@ Range<T> SubRange(const Range<T>& full, const size_t offset,
|
||||
return Range<T>{newBegin, newBegin + length};
|
||||
}
|
||||
|
||||
static inline size_t SizeOfViewElem(const dom::ArrayBufferView& view) {
|
||||
const auto& elemType = view.Type();
|
||||
if (elemType == js::Scalar::MaxTypedArrayViewType) // DataViews.
|
||||
return 1;
|
||||
|
||||
return js::Scalar::byteSize(elemType);
|
||||
}
|
||||
|
||||
Maybe<Range<const uint8_t>> GetRangeFromView(const dom::ArrayBufferView& view,
|
||||
Maybe<Range<const uint8_t>> GetRangeFromData(const Span<uint8_t>& data,
|
||||
size_t bytesPerElem,
|
||||
GLuint elemOffset,
|
||||
GLuint elemCountOverride) {
|
||||
const auto byteRange = MakeRangeAbv(view); // In bytes.
|
||||
const auto bytesPerElem = SizeOfViewElem(view);
|
||||
const auto byteRange = Range(data); // In bytes.
|
||||
|
||||
auto elemCount = byteRange.length() / bytesPerElem;
|
||||
if (elemOffset > elemCount) return {};
|
||||
@ -4200,15 +4249,6 @@ void ClientWebGLContext::TexImage(uint8_t funcDims, GLenum imageTarget,
|
||||
|
||||
// -
|
||||
|
||||
// Demarcate the region within which GC is disallowed. Typed arrays can move
|
||||
// their data during a GC, so this will allow the rooting hazard analysis to
|
||||
// report if a GC is possible while any data pointers extracted from the
|
||||
// typed array are still live.
|
||||
dom::Uint8ClampedArray scopedArr;
|
||||
const auto reset = MakeScopeExit([&] {
|
||||
scopedArr.Reset(); // (For the hazard analysis) Done with the data.
|
||||
});
|
||||
|
||||
// -
|
||||
bool isDataUpload = false;
|
||||
auto desc = [&]() -> Maybe<webgl::TexUnpackBlobDesc> {
|
||||
@ -4240,9 +4280,14 @@ void ClientWebGLContext::TexImage(uint8_t funcDims, GLenum imageTarget,
|
||||
break;
|
||||
}
|
||||
|
||||
const auto range = GetRangeFromView(view, src.mViewElemOffset,
|
||||
return view.ProcessData(
|
||||
[&](const Span<uint8_t>& aData,
|
||||
JS::AutoCheckCannotGC&& nogc) -> Maybe<webgl::TexUnpackBlobDesc> {
|
||||
const auto range = GetRangeFromData(aData, SizeOfViewElem(view),
|
||||
src.mViewElemOffset,
|
||||
src.mViewElemLengthOverride);
|
||||
if (!range) {
|
||||
nogc.reset();
|
||||
EnqueueError(LOCAL_GL_INVALID_OPERATION, "`source` too small.");
|
||||
return {};
|
||||
}
|
||||
@ -4251,6 +4296,7 @@ void ClientWebGLContext::TexImage(uint8_t funcDims, GLenum imageTarget,
|
||||
gfxAlphaType::NonPremult,
|
||||
Some(RawBuffer<>{*range}),
|
||||
{}});
|
||||
});
|
||||
}
|
||||
|
||||
if (src.mImageBitmap) {
|
||||
@ -4260,13 +4306,18 @@ void ClientWebGLContext::TexImage(uint8_t funcDims, GLenum imageTarget,
|
||||
|
||||
if (src.mImageData) {
|
||||
const auto& imageData = *src.mImageData;
|
||||
dom::Uint8ClampedArray scopedArr;
|
||||
MOZ_RELEASE_ASSERT(scopedArr.Init(imageData.GetDataObject()));
|
||||
scopedArr.ComputeState();
|
||||
const auto dataSize = scopedArr.Length();
|
||||
const auto data = reinterpret_cast<uint8_t*>(scopedArr.Data());
|
||||
if (!data) {
|
||||
// Neutered, e.g. via Transfer
|
||||
EnqueueError(LOCAL_GL_INVALID_VALUE,
|
||||
|
||||
return scopedArr.ProcessData(
|
||||
[&](const Span<uint8_t>& aData,
|
||||
JS::AutoCheckCannotGC&& nogc) -> Maybe<webgl::TexUnpackBlobDesc> {
|
||||
const auto dataSize = aData.Length();
|
||||
const auto data = aData.Elements();
|
||||
if (dataSize == 0) {
|
||||
nogc.reset(); // aData will not be used.
|
||||
EnqueueError(
|
||||
LOCAL_GL_INVALID_VALUE,
|
||||
"ImageData.data.buffer is Detached. (Maybe you Transfered "
|
||||
"it to a Worker?");
|
||||
return {};
|
||||
@ -4293,9 +4344,10 @@ void ClientWebGLContext::TexImage(uint8_t funcDims, GLenum imageTarget,
|
||||
size.valueOr(uvec3{imageUSize.x, imageUSize.y, 1});
|
||||
|
||||
// WhatWG "HTML Living Standard" (30 October 2015):
|
||||
// "The getImageData(sx, sy, sw, sh) method [...] Pixels must be returned
|
||||
// as non-premultiplied alpha values."
|
||||
return Some(webgl::TexUnpackBlobDesc{imageTarget,
|
||||
// "The getImageData(sx, sy, sw, sh) method [...] Pixels must be
|
||||
// returned as non-premultiplied alpha values."
|
||||
auto result =
|
||||
Some(webgl::TexUnpackBlobDesc{imageTarget,
|
||||
concreteSize,
|
||||
gfxAlphaType::NonPremult,
|
||||
{},
|
||||
@ -4304,6 +4356,9 @@ void ClientWebGLContext::TexImage(uint8_t funcDims, GLenum imageTarget,
|
||||
nullptr,
|
||||
{},
|
||||
surf});
|
||||
nogc.reset(); // Done with aData
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
if (src.mOffscreenCanvas) {
|
||||
@ -4563,29 +4618,40 @@ void ClientWebGLContext::CompressedTexImage(bool sub, uint8_t funcDims,
|
||||
return;
|
||||
}
|
||||
|
||||
RawBuffer<> range;
|
||||
Maybe<uint64_t> pboOffset;
|
||||
if (src.mView) {
|
||||
const auto maybe = GetRangeFromView(*src.mView, src.mViewElemOffset,
|
||||
src.mViewElemLengthOverride);
|
||||
if (!maybe) {
|
||||
src.mView->ProcessData([&](const Span<uint8_t>& aData,
|
||||
JS::AutoCheckCannotGC&& aNoGC) {
|
||||
const auto range =
|
||||
GetRangeFromData(aData, SizeOfViewElem(*src.mView),
|
||||
src.mViewElemOffset, src.mViewElemLengthOverride);
|
||||
if (!range) {
|
||||
aNoGC.reset();
|
||||
EnqueueError(LOCAL_GL_INVALID_VALUE, "`source` too small.");
|
||||
return;
|
||||
}
|
||||
range = RawBuffer<>{*maybe};
|
||||
} else if (src.mPboOffset) {
|
||||
if (!ValidateNonNegative("offset", *src.mPboOffset)) return;
|
||||
pboOffset = Some(*src.mPboOffset);
|
||||
} else {
|
||||
|
||||
// We don't need to shrink `range` because valid calls require
|
||||
// `range` to match requirements exactly.
|
||||
|
||||
RunWithGCData<RPROC(CompressedTexImage)>(
|
||||
std::move(aNoGC), sub, imageTarget, static_cast<uint32_t>(level),
|
||||
format, CastUvec3(offset), CastUvec3(isize), RawBuffer<>{*range},
|
||||
static_cast<uint32_t>(pboImageSize), Maybe<uint64_t>());
|
||||
return;
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (!src.mPboOffset) {
|
||||
MOZ_CRASH("impossible");
|
||||
}
|
||||
|
||||
// We don't need to shrink `range` because valid calls require `range` to
|
||||
// match requirements exactly.
|
||||
if (!ValidateNonNegative("offset", *src.mPboOffset)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Run<RPROC(CompressedTexImage)>(
|
||||
sub, imageTarget, static_cast<uint32_t>(level), format, CastUvec3(offset),
|
||||
CastUvec3(isize), range, static_cast<uint32_t>(pboImageSize), pboOffset);
|
||||
CastUvec3(isize), RawBuffer<>(), static_cast<uint32_t>(pboImageSize),
|
||||
Some(*src.mPboOffset));
|
||||
}
|
||||
|
||||
void ClientWebGLContext::CopyTexImage(uint8_t funcDims, GLenum imageTarget,
|
||||
@ -4762,13 +4828,21 @@ void ClientWebGLContext::UniformData(const GLenum funcElemType,
|
||||
const WebGLUniformLocationJS* const loc,
|
||||
bool transpose,
|
||||
const Range<const uint8_t>& bytes,
|
||||
JS::AutoCheckCannotGC&& nogc,
|
||||
GLuint elemOffset,
|
||||
GLuint elemCountOverride) const {
|
||||
// FuncScope::~FuncScope() can GC in a failure case, so all `return`
|
||||
// statements need to `nogc.reset()` up until the `nogc` is consumed by
|
||||
// `RunWithGCData`.
|
||||
const FuncScope funcScope(*this, "uniform setter");
|
||||
if (IsContextLost()) return;
|
||||
if (IsContextLost()) {
|
||||
nogc.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& activeLinkResult = GetActiveLinkResult();
|
||||
if (!activeLinkResult) {
|
||||
nogc.reset();
|
||||
EnqueueError(LOCAL_GL_INVALID_OPERATION, "No active linked Program.");
|
||||
return;
|
||||
}
|
||||
@ -4777,12 +4851,14 @@ void ClientWebGLContext::UniformData(const GLenum funcElemType,
|
||||
|
||||
auto availCount = bytes.length() / sizeof(float);
|
||||
if (elemOffset > availCount) {
|
||||
nogc.reset();
|
||||
EnqueueError(LOCAL_GL_INVALID_VALUE, "`elemOffset` too large for `data`.");
|
||||
return;
|
||||
}
|
||||
availCount -= elemOffset;
|
||||
if (elemCountOverride) {
|
||||
if (elemCountOverride > availCount) {
|
||||
nogc.reset();
|
||||
EnqueueError(LOCAL_GL_INVALID_VALUE,
|
||||
"`elemCountOverride` too large for `data`.");
|
||||
return;
|
||||
@ -4794,6 +4870,7 @@ void ClientWebGLContext::UniformData(const GLenum funcElemType,
|
||||
|
||||
const auto channels = ElemTypeComponents(funcElemType);
|
||||
if (!availCount || availCount % channels != 0) {
|
||||
nogc.reset();
|
||||
EnqueueError(LOCAL_GL_INVALID_VALUE,
|
||||
"`values` length (%u) must be a positive "
|
||||
"integer multiple of size of %s.",
|
||||
@ -4806,12 +4883,16 @@ void ClientWebGLContext::UniformData(const GLenum funcElemType,
|
||||
uint32_t locId = -1;
|
||||
if (MOZ_LIKELY(loc)) {
|
||||
locId = loc->mLocation;
|
||||
if (!loc->ValidateUsable(*this, "location")) return;
|
||||
if (!loc->ValidateUsable(*this, "location")) {
|
||||
nogc.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
// -
|
||||
|
||||
const auto& reqLinkInfo = loc->mParent.lock();
|
||||
if (reqLinkInfo.get() != activeLinkResult) {
|
||||
nogc.reset();
|
||||
EnqueueError(LOCAL_GL_INVALID_OPERATION,
|
||||
"UniformLocation is not from the current active Program.");
|
||||
return;
|
||||
@ -4831,6 +4912,7 @@ void ClientWebGLContext::UniformData(const GLenum funcElemType,
|
||||
}
|
||||
validSetters.pop_back(); // Cheekily discard the extra trailing '/'.
|
||||
|
||||
nogc.reset();
|
||||
EnqueueError(LOCAL_GL_INVALID_OPERATION,
|
||||
"Uniform's `type` requires uniform setter of type %s.",
|
||||
validSetters.c_str());
|
||||
@ -4844,7 +4926,8 @@ void ClientWebGLContext::UniformData(const GLenum funcElemType,
|
||||
reinterpret_cast<const webgl::UniformDataVal*>(bytes.begin().get()) +
|
||||
elemOffset;
|
||||
const auto range = Range{begin, availCount};
|
||||
Run<RPROC(UniformData)>(locId, transpose, RawBuffer{range});
|
||||
RunWithGCData<RPROC(UniformData)>(std::move(nogc), locId, transpose,
|
||||
RawBuffer{range});
|
||||
}
|
||||
|
||||
// -
|
||||
@ -5044,10 +5127,11 @@ void ClientWebGLContext::ReadPixels(GLint x, GLint y, GLsizei width,
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t* bytes;
|
||||
size_t byteLen;
|
||||
if (!ValidateArrayBufferView(dstData, dstElemOffset, 0,
|
||||
LOCAL_GL_INVALID_VALUE, &bytes, &byteLen)) {
|
||||
size_t elemSize = SizeOfViewElem(dstData);
|
||||
dstData.ProcessFixedData([&](const Span<uint8_t>& aData) {
|
||||
const auto& range = ValidateArrayBufferView(aData, elemSize, dstElemOffset,
|
||||
0, LOCAL_GL_INVALID_VALUE);
|
||||
if (!range) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -5055,10 +5139,8 @@ void ClientWebGLContext::ReadPixels(GLint x, GLint y, GLsizei width,
|
||||
*uvec2::From(width, height),
|
||||
{format, type},
|
||||
state.mPixelPackState};
|
||||
const auto range = Range<uint8_t>(bytes, byteLen);
|
||||
if (!DoReadPixels(desc, range)) {
|
||||
return;
|
||||
}
|
||||
(void)DoReadPixels(desc, *range);
|
||||
});
|
||||
}
|
||||
|
||||
bool ClientWebGLContext::DoReadPixels(const webgl::ReadPixelsDesc& desc,
|
||||
@ -6566,34 +6648,26 @@ const webgl::LinkResult& ClientWebGLContext::GetLinkResult(
|
||||
|
||||
// ---------------------------
|
||||
|
||||
bool ClientWebGLContext::ValidateArrayBufferView(
|
||||
const dom::ArrayBufferView& view, GLuint elemOffset,
|
||||
GLuint elemCountOverride, const GLenum errorEnum, uint8_t** const out_bytes,
|
||||
size_t* const out_byteLen) const {
|
||||
view.ComputeState();
|
||||
uint8_t* const bytes = view.Data();
|
||||
const size_t byteLen = view.Length();
|
||||
|
||||
const auto& elemSize = SizeOfViewElem(view);
|
||||
|
||||
size_t elemCount = byteLen / elemSize;
|
||||
Maybe<Range<uint8_t>> ClientWebGLContext::ValidateArrayBufferView(
|
||||
const Span<uint8_t>& bytes, size_t elemSize, GLuint elemOffset,
|
||||
GLuint elemCountOverride, const GLenum errorEnum) const {
|
||||
size_t elemCount = bytes.Length() / elemSize;
|
||||
if (elemOffset > elemCount) {
|
||||
EnqueueError(errorEnum, "Invalid offset into ArrayBufferView.");
|
||||
return false;
|
||||
return Nothing();
|
||||
}
|
||||
elemCount -= elemOffset;
|
||||
|
||||
if (elemCountOverride) {
|
||||
if (elemCountOverride > elemCount) {
|
||||
EnqueueError(errorEnum, "Invalid sub-length for ArrayBufferView.");
|
||||
return false;
|
||||
return Nothing();
|
||||
}
|
||||
elemCount = elemCountOverride;
|
||||
}
|
||||
|
||||
*out_bytes = bytes + (elemOffset * elemSize);
|
||||
*out_byteLen = elemCount * elemSize;
|
||||
return true;
|
||||
return Some(Range<uint8_t>(
|
||||
bytes.Subspan(elemOffset * elemSize, elemCount * elemSize)));
|
||||
}
|
||||
|
||||
// ---------------------------
|
||||
|
@ -7,9 +7,11 @@
|
||||
#define CLIENTWEBGLCONTEXT_H_
|
||||
|
||||
#include "GLConsts.h"
|
||||
#include "js/GCAPI.h"
|
||||
#include "mozilla/dom/ImageData.h"
|
||||
#include "mozilla/Range.h"
|
||||
#include "mozilla/RefCounted.h"
|
||||
#include "mozilla/dom/TypedArray.h"
|
||||
#include "nsICanvasRenderingContextInternal.h"
|
||||
#include "nsWrapperCache.h"
|
||||
#include "mozilla/dom/WebGLRenderingContextBinding.h"
|
||||
@ -24,6 +26,7 @@
|
||||
#include "WebGLCommandQueue.h"
|
||||
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
@ -618,22 +621,47 @@ using Float32ListU = dom::MaybeSharedFloat32ArrayOrUnrestrictedFloatSequence;
|
||||
using Int32ListU = dom::MaybeSharedInt32ArrayOrLongSequence;
|
||||
using Uint32ListU = dom::MaybeSharedUint32ArrayOrUnsignedLongSequence;
|
||||
|
||||
inline Range<const float> MakeRange(const Float32ListU& list) {
|
||||
if (list.IsFloat32Array()) return MakeRangeAbv(list.GetAsFloat32Array());
|
||||
|
||||
return MakeRange(list.GetAsUnrestrictedFloatSequence());
|
||||
template <typename Converter, typename T>
|
||||
inline bool ConvertSequence(const dom::Sequence<T>& sequence,
|
||||
Converter&& converter) {
|
||||
// It's ok to GC here, but we need a common parameter list for
|
||||
// Converter with the typed array version.
|
||||
JS::AutoCheckCannotGC nogc;
|
||||
nogc.reset();
|
||||
return std::forward<Converter>(converter)(Span(sequence), std::move(nogc));
|
||||
}
|
||||
|
||||
inline Range<const int32_t> MakeRange(const Int32ListU& list) {
|
||||
if (list.IsInt32Array()) return MakeRangeAbv(list.GetAsInt32Array());
|
||||
|
||||
return MakeRange(list.GetAsLongSequence());
|
||||
template <typename Converter>
|
||||
inline bool Convert(const Float32ListU& list, Converter&& converter) {
|
||||
if (list.IsFloat32Array()) {
|
||||
return list.GetAsFloat32Array().ProcessData(
|
||||
std::forward<Converter>(converter));
|
||||
}
|
||||
|
||||
inline Range<const uint32_t> MakeRange(const Uint32ListU& list) {
|
||||
if (list.IsUint32Array()) return MakeRangeAbv(list.GetAsUint32Array());
|
||||
return ConvertSequence(list.GetAsUnrestrictedFloatSequence(),
|
||||
std::forward<Converter>(converter));
|
||||
}
|
||||
|
||||
return MakeRange(list.GetAsUnsignedLongSequence());
|
||||
template <typename Converter>
|
||||
inline bool Convert(const Int32ListU& list, Converter&& converter) {
|
||||
if (list.IsInt32Array()) {
|
||||
return list.GetAsInt32Array().ProcessData(
|
||||
std::forward<Converter>(converter));
|
||||
}
|
||||
|
||||
return ConvertSequence(list.GetAsLongSequence(),
|
||||
std::forward<Converter>(converter));
|
||||
}
|
||||
|
||||
template <typename Converter>
|
||||
inline bool Convert(const Uint32ListU& list, Converter&& converter) {
|
||||
if (list.IsUint32Array()) {
|
||||
return list.GetAsUint32Array().ProcessData(
|
||||
std::forward<Converter>(converter));
|
||||
}
|
||||
|
||||
return ConvertSequence(list.GetAsUnsignedLongSequence(),
|
||||
std::forward<Converter>(converter));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
@ -644,6 +672,11 @@ inline Range<const uint8_t> MakeByteRange(const T& x) {
|
||||
typed.length() * sizeof(typed[0]));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline Range<const uint8_t> MakeByteRange(const Span<T>& x) {
|
||||
return AsBytes(x);
|
||||
}
|
||||
|
||||
// -
|
||||
|
||||
struct TexImageSourceAdapter final : public TexImageSource {
|
||||
@ -898,11 +931,11 @@ class ClientWebGLContext final : public nsICanvasRenderingContextInternal,
|
||||
void EnqueueErrorImpl(GLenum errorOrZero, const nsACString&) const;
|
||||
|
||||
public:
|
||||
bool ValidateArrayBufferView(const dom::ArrayBufferView& view,
|
||||
GLuint elemOffset, GLuint elemCountOverride,
|
||||
const GLenum errorEnum,
|
||||
uint8_t** const out_bytes,
|
||||
size_t* const out_byteLen) const;
|
||||
Maybe<Range<uint8_t>> ValidateArrayBufferView(const Span<uint8_t>& bytes,
|
||||
size_t elemSize,
|
||||
GLuint elemOffset,
|
||||
GLuint elemCountOverride,
|
||||
const GLenum errorEnum) const;
|
||||
|
||||
protected:
|
||||
template <typename T>
|
||||
@ -1297,24 +1330,48 @@ class ClientWebGLContext final : public nsICanvasRenderingContextInternal,
|
||||
// -
|
||||
|
||||
private:
|
||||
void ClearBufferTv(GLenum buffer, GLint drawBuffer, webgl::AttribBaseType,
|
||||
const Range<const uint8_t>& view, GLuint srcElemOffset);
|
||||
void ClearBufferTv(const GLenum buffer, const GLint drawBuffer,
|
||||
const webgl::AttribBaseType type,
|
||||
JS::AutoCheckCannotGC&& nogc,
|
||||
const Span<const uint8_t>& view,
|
||||
const GLuint srcElemOffset);
|
||||
|
||||
public:
|
||||
void ClearBufferfv(GLenum buffer, GLint drawBuffer, const Float32ListU& list,
|
||||
GLuint srcElemOffset) {
|
||||
const FuncScope funcScope(*this, "clearBufferfv");
|
||||
if (!Convert(list, [&](const Span<const float>& aData,
|
||||
JS::AutoCheckCannotGC&& nogc) {
|
||||
ClearBufferTv(buffer, drawBuffer, webgl::AttribBaseType::Float,
|
||||
MakeByteRange(list), srcElemOffset);
|
||||
std::move(nogc), AsBytes(aData), srcElemOffset);
|
||||
return true;
|
||||
})) {
|
||||
EnqueueError(LOCAL_GL_INVALID_VALUE, "`values` too small.");
|
||||
}
|
||||
}
|
||||
void ClearBufferiv(GLenum buffer, GLint drawBuffer, const Int32ListU& list,
|
||||
GLuint srcElemOffset) {
|
||||
const FuncScope funcScope(*this, "clearBufferiv");
|
||||
if (!Convert(list, [&](const Span<const int32_t>& aData,
|
||||
JS::AutoCheckCannotGC&& nogc) {
|
||||
ClearBufferTv(buffer, drawBuffer, webgl::AttribBaseType::Int,
|
||||
MakeByteRange(list), srcElemOffset);
|
||||
std::move(nogc), AsBytes(aData), srcElemOffset);
|
||||
return true;
|
||||
})) {
|
||||
EnqueueError(LOCAL_GL_INVALID_VALUE, "`values` too small.");
|
||||
}
|
||||
}
|
||||
void ClearBufferuiv(GLenum buffer, GLint drawBuffer, const Uint32ListU& list,
|
||||
GLuint srcElemOffset) {
|
||||
const FuncScope funcScope(*this, "clearBufferuiv");
|
||||
if (!Convert(list, [&](const Span<const uint32_t>& aData,
|
||||
JS::AutoCheckCannotGC&& nogc) {
|
||||
ClearBufferTv(buffer, drawBuffer, webgl::AttribBaseType::Uint,
|
||||
MakeByteRange(list), srcElemOffset);
|
||||
std::move(nogc), AsBytes(aData), srcElemOffset);
|
||||
return true;
|
||||
})) {
|
||||
EnqueueError(LOCAL_GL_INVALID_VALUE, "`values` too small.");
|
||||
}
|
||||
}
|
||||
|
||||
// -
|
||||
@ -1840,9 +1897,26 @@ class ClientWebGLContext final : public nsICanvasRenderingContextInternal,
|
||||
return state.mActiveLinkResult.get();
|
||||
}
|
||||
|
||||
// Used by callers that have a `nogc` token that ensures that no GC will
|
||||
// happen while `bytes` is alive. Internally, the nogc range will be ended
|
||||
// after `bytes` is used (either successfully but where the data are possibly
|
||||
// sent over IPC which has a tendency to GC, or unsuccesfully in which case
|
||||
// error handling can GC.)
|
||||
void UniformData(GLenum funcElemType, const WebGLUniformLocationJS* const loc,
|
||||
bool transpose, const Range<const uint8_t>& bytes,
|
||||
GLuint elemOffset = 0, GLuint elemCountOverride = 0) const;
|
||||
JS::AutoCheckCannotGC&& nogc, GLuint elemOffset = 0,
|
||||
GLuint elemCountOverride = 0) const;
|
||||
|
||||
// Used by callers that are not passing `bytes` that might be GC-controlled.
|
||||
// This will create an artificial and unnecessary nogc region that should
|
||||
// get optimized away to nothing.
|
||||
void UniformData(GLenum funcElemType, const WebGLUniformLocationJS* const loc,
|
||||
bool transpose, const Range<const uint8_t>& bytes,
|
||||
GLuint elemOffset = 0, GLuint elemCountOverride = 0) const {
|
||||
JS::AutoCheckCannotGC nogc;
|
||||
UniformData(funcElemType, loc, transpose, bytes, std::move(nogc),
|
||||
elemOffset, elemCountOverride);
|
||||
}
|
||||
|
||||
// -
|
||||
|
||||
@ -1902,8 +1976,11 @@ class ClientWebGLContext final : public nsICanvasRenderingContextInternal,
|
||||
void Uniform##NT##v(const WebGLUniformLocationJS* const loc, \
|
||||
const TypeListU& list, GLuint elemOffset = 0, \
|
||||
GLuint elemCountOverride = 0) const { \
|
||||
UniformData(TYPE, loc, false, MakeByteRange(list), elemOffset, \
|
||||
elemCountOverride); \
|
||||
Convert(list, [&](const auto& aData, JS::AutoCheckCannotGC&& nogc) { \
|
||||
UniformData(TYPE, loc, false, MakeByteRange(aData), std::move(nogc), \
|
||||
elemOffset, elemCountOverride); \
|
||||
return true; \
|
||||
}); \
|
||||
}
|
||||
|
||||
_(1f, Float32ListU, LOCAL_GL_FLOAT)
|
||||
@ -1927,8 +2004,12 @@ class ClientWebGLContext final : public nsICanvasRenderingContextInternal,
|
||||
void UniformMatrix##X##fv(const WebGLUniformLocationJS* loc, bool transpose, \
|
||||
const Float32ListU& list, GLuint elemOffset = 0, \
|
||||
GLuint elemCountOverride = 0) const { \
|
||||
UniformData(LOCAL_GL_FLOAT_MAT##X, loc, transpose, MakeByteRange(list), \
|
||||
elemOffset, elemCountOverride); \
|
||||
Convert(list, [&](const Span<const float>& aData, \
|
||||
JS::AutoCheckCannotGC&& nogc) { \
|
||||
UniformData(LOCAL_GL_FLOAT_MAT##X, loc, transpose, MakeByteRange(aData), \
|
||||
std::move(nogc), elemOffset, elemCountOverride); \
|
||||
return true; \
|
||||
}); \
|
||||
}
|
||||
|
||||
_(2)
|
||||
@ -1978,53 +2059,88 @@ class ClientWebGLContext final : public nsICanvasRenderingContextInternal,
|
||||
|
||||
// -
|
||||
|
||||
template <typename List, typename T, size_t N>
|
||||
bool MakeArrayFromList(const List& list, T (&array)[N]) {
|
||||
bool badLength = false;
|
||||
if (!Convert(list, [&](const auto& aData, JS::AutoCheckCannotGC&&) {
|
||||
static_assert(
|
||||
std::is_same_v<std::remove_const_t<
|
||||
std::remove_reference_t<decltype(aData[0])>>,
|
||||
T>);
|
||||
if (N > aData.Length()) {
|
||||
badLength = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::copy_n(aData.begin(), N, array);
|
||||
return true;
|
||||
})) {
|
||||
EnqueueError(
|
||||
LOCAL_GL_INVALID_VALUE,
|
||||
badLength
|
||||
? nsPrintfCString("Length of `list` must be >=%zu.", N).get()
|
||||
: "Conversion of `list` failed.");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void VertexAttrib1fv(const GLuint index, const Float32ListU& list) {
|
||||
const FuncScope funcScope(*this, "vertexAttrib1fv");
|
||||
if (IsContextLost()) return;
|
||||
|
||||
const auto range = MakeRange(list);
|
||||
if (range.length() < 1) {
|
||||
EnqueueError(LOCAL_GL_INVALID_VALUE, "Length of `list` must be >=1.");
|
||||
float arr[1];
|
||||
if (!MakeArrayFromList(list, arr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
VertexAttrib1f(index, range[0]);
|
||||
VertexAttrib1f(index, arr[0]);
|
||||
}
|
||||
|
||||
void VertexAttrib2fv(const GLuint index, const Float32ListU& list) {
|
||||
const FuncScope funcScope(*this, "vertexAttrib1fv");
|
||||
if (IsContextLost()) return;
|
||||
|
||||
const auto range = MakeRange(list);
|
||||
if (range.length() < 2) {
|
||||
EnqueueError(LOCAL_GL_INVALID_VALUE, "Length of `list` must be >=2.");
|
||||
float arr[2];
|
||||
if (!MakeArrayFromList(list, arr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
VertexAttrib2f(index, range[0], range[1]);
|
||||
VertexAttrib2f(index, arr[0], arr[1]);
|
||||
}
|
||||
|
||||
void VertexAttrib3fv(const GLuint index, const Float32ListU& list) {
|
||||
const FuncScope funcScope(*this, "vertexAttrib1fv");
|
||||
if (IsContextLost()) return;
|
||||
|
||||
const auto range = MakeRange(list);
|
||||
if (range.length() < 3) {
|
||||
EnqueueError(LOCAL_GL_INVALID_VALUE, "Length of `list` must be >=3.");
|
||||
float arr[3];
|
||||
if (!MakeArrayFromList(list, arr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
VertexAttrib3f(index, range[0], range[1], range[2]);
|
||||
VertexAttrib3f(index, arr[0], arr[1], arr[2]);
|
||||
}
|
||||
|
||||
void VertexAttrib4fv(GLuint index, const Float32ListU& list) {
|
||||
VertexAttrib4Tv(index, webgl::AttribBaseType::Float, MakeByteRange(list));
|
||||
const FuncScope funcScope(*this, "vertexAttrib4fv");
|
||||
float arr[4];
|
||||
if (!MakeArrayFromList(list, arr)) {
|
||||
return;
|
||||
}
|
||||
VertexAttrib4Tv(index, webgl::AttribBaseType::Float, MakeByteRange(arr));
|
||||
}
|
||||
void VertexAttribI4iv(GLuint index, const Int32ListU& list) {
|
||||
VertexAttrib4Tv(index, webgl::AttribBaseType::Int, MakeByteRange(list));
|
||||
const FuncScope funcScope(*this, "vertexAttribI4iv");
|
||||
int32_t arr[4];
|
||||
if (!MakeArrayFromList(list, arr)) {
|
||||
return;
|
||||
}
|
||||
VertexAttrib4Tv(index, webgl::AttribBaseType::Int, MakeByteRange(arr));
|
||||
}
|
||||
void VertexAttribI4uiv(GLuint index, const Uint32ListU& list) {
|
||||
VertexAttrib4Tv(index, webgl::AttribBaseType::Uint, MakeByteRange(list));
|
||||
const FuncScope funcScope(*this, "vertexAttribI4uiv");
|
||||
uint32_t arr[4];
|
||||
if (!MakeArrayFromList(list, arr)) {
|
||||
return;
|
||||
}
|
||||
VertexAttrib4Tv(index, webgl::AttribBaseType::Uint, MakeByteRange(arr));
|
||||
}
|
||||
|
||||
void VertexAttribI4i(GLuint index, GLint x, GLint y, GLint z, GLint w) {
|
||||
@ -2215,6 +2331,11 @@ class ClientWebGLContext final : public nsICanvasRenderingContextInternal,
|
||||
template <typename MethodType, MethodType method, typename... Args>
|
||||
void Run(Args&&... aArgs) const;
|
||||
|
||||
// Same as above for use when using potentially GC-controlled data. The scope
|
||||
// of `aNoGC` will be ended after the data is no longer needed.
|
||||
template <typename MethodType, MethodType method, typename... Args>
|
||||
void RunWithGCData(JS::AutoCheckCannotGC&& aNoGC, Args&&... aArgs) const;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Helpers for DOM operations, composition, actors, etc
|
||||
// -------------------------------------------------------------------------
|
||||
|
@ -1337,7 +1337,6 @@ already_AddRefed<ImageBitmap> ImageBitmap::CreateInternal(
|
||||
"failed?)");
|
||||
return nullptr;
|
||||
}
|
||||
array.ComputeState();
|
||||
const SurfaceFormat FORMAT = SurfaceFormat::R8G8B8A8;
|
||||
// ImageData's underlying data is not alpha-premultiplied.
|
||||
auto alphaType = (aOptions.mPremultiplyAlpha == PremultiplyAlpha::Premultiply)
|
||||
@ -1348,7 +1347,6 @@ already_AddRefed<ImageBitmap> ImageBitmap::CreateInternal(
|
||||
const uint32_t imageWidth = aImageData.Width();
|
||||
const uint32_t imageHeight = aImageData.Height();
|
||||
const uint32_t imageStride = imageWidth * BYTES_PER_PIXEL;
|
||||
const uint32_t dataLength = array.Length();
|
||||
const gfx::IntSize imageSize(imageWidth, imageHeight);
|
||||
|
||||
// Check the ImageData is neutered or not.
|
||||
@ -1357,6 +1355,9 @@ already_AddRefed<ImageBitmap> ImageBitmap::CreateInternal(
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return array.ProcessFixedData(
|
||||
[&](const Span<const uint8_t>& aData) -> already_AddRefed<ImageBitmap> {
|
||||
const uint32_t dataLength = aData.Length();
|
||||
if ((imageWidth * imageHeight * BYTES_PER_PIXEL) != dataLength) {
|
||||
aRv.ThrowInvalidStateError("Data size / image format mismatch");
|
||||
return nullptr;
|
||||
@ -1365,26 +1366,17 @@ already_AddRefed<ImageBitmap> ImageBitmap::CreateInternal(
|
||||
// Create and Crop the raw data into a layers::Image
|
||||
RefPtr<layers::Image> data;
|
||||
|
||||
// If the data could move during a GC, copy it out into a local buffer that
|
||||
// lives until a CreateImageFromRawData lower in the stack copies it.
|
||||
// Reassure the static analysis that we know what we're doing.
|
||||
size_t maxInline = JS_MaxMovableTypedArraySize();
|
||||
uint8_t inlineDataBuffer[maxInline];
|
||||
uint8_t* fixedData = array.FixedData(inlineDataBuffer, maxInline);
|
||||
|
||||
// Lie to the hazard analysis and say that we're done with everything that
|
||||
// `array` was using (safe because the data buffer is fixed, and the holding
|
||||
// JSObject is being kept alive elsewhere.)
|
||||
array.Reset();
|
||||
uint8_t* fixedData = const_cast<uint8_t*>(aData.Elements());
|
||||
|
||||
if (NS_IsMainThread()) {
|
||||
data = CreateImageFromRawData(imageSize, imageStride, FORMAT, fixedData,
|
||||
data =
|
||||
CreateImageFromRawData(imageSize, imageStride, FORMAT, fixedData,
|
||||
dataLength, aCropRect, aOptions);
|
||||
} else {
|
||||
RefPtr<CreateImageFromRawDataInMainThreadSyncTask> task =
|
||||
new CreateImageFromRawDataInMainThreadSyncTask(
|
||||
fixedData, dataLength, imageStride, FORMAT, imageSize, aCropRect,
|
||||
getter_AddRefs(data), aOptions);
|
||||
fixedData, dataLength, imageStride, FORMAT, imageSize,
|
||||
aCropRect, getter_AddRefs(data), aOptions);
|
||||
task->Dispatch(Canceling, aRv);
|
||||
}
|
||||
|
||||
@ -1396,13 +1388,13 @@ already_AddRefed<ImageBitmap> ImageBitmap::CreateInternal(
|
||||
// Create an ImageBitmap.
|
||||
RefPtr<ImageBitmap> ret =
|
||||
new ImageBitmap(aGlobal, data, false /* write-only */, alphaType);
|
||||
|
||||
ret->mAllocatedImageData = true;
|
||||
|
||||
// The cropping information has been handled in the CreateImageFromRawData()
|
||||
// function.
|
||||
// The cropping information has been handled in the
|
||||
// CreateImageFromRawData() function.
|
||||
|
||||
return ret.forget();
|
||||
});
|
||||
}
|
||||
|
||||
/* static */
|
||||
|
@ -51,8 +51,7 @@ already_AddRefed<ImageData> ImageData::Constructor(const GlobalObject& aGlobal,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Restrict the typed array length to INT32_MAX because that's all we support
|
||||
// in dom::TypedArray::ComputeState.
|
||||
// Restrict the typed array length to INT32_MAX because that's all we support.
|
||||
CheckedInt<uint32_t> length = CheckedInt<uint32_t>(aWidth) * aHeight * 4;
|
||||
if (!length.isValid() || length.value() > INT32_MAX) {
|
||||
aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
|
||||
@ -73,9 +72,11 @@ already_AddRefed<ImageData> ImageData::Constructor(
|
||||
const GlobalObject& aGlobal, const Uint8ClampedArray& aData,
|
||||
const uint32_t aWidth, const Optional<uint32_t>& aHeight,
|
||||
ErrorResult& aRv) {
|
||||
aData.ComputeState();
|
||||
|
||||
uint32_t length = aData.Length();
|
||||
Maybe<uint32_t> maybeLength = aData.ProcessData(
|
||||
[&](const Span<uint8_t>& aData, JS::AutoCheckCannotGC&& nogc) {
|
||||
return Some(aData.Length());
|
||||
});
|
||||
uint32_t length = maybeLength.valueOr(0);
|
||||
if (length == 0 || length % 4) {
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return nullptr;
|
||||
|
@ -1108,14 +1108,6 @@ inline Range<const T> MakeRange(const RawBuffer<T>& from) {
|
||||
return from.Data();
|
||||
}
|
||||
|
||||
// abv = ArrayBufferView
|
||||
template <typename T>
|
||||
inline auto MakeRangeAbv(const T& abv)
|
||||
-> Range<const typename T::element_type> {
|
||||
abv.ComputeState();
|
||||
return {abv.Data(), abv.Length()};
|
||||
}
|
||||
|
||||
// -
|
||||
|
||||
constexpr auto kUniversalAlignment = alignof(std::max_align_t);
|
||||
@ -1134,10 +1126,6 @@ inline size_t ByteSize(const Range<T>& range) {
|
||||
return range.length() * sizeof(T);
|
||||
}
|
||||
|
||||
Maybe<Range<const uint8_t>> GetRangeFromView(const dom::ArrayBufferView& view,
|
||||
GLuint elemOffset,
|
||||
GLuint elemCountOverride);
|
||||
|
||||
// -
|
||||
|
||||
template <typename T>
|
||||
|
@ -45,7 +45,6 @@ struct RsaHashedKeyAlgorithmStorage {
|
||||
aRsa.mModulusLength = mModulusLength;
|
||||
aRsa.mHash.mName = mHash.mName;
|
||||
aRsa.mPublicExponent.Init(exponent);
|
||||
aRsa.mPublicExponent.ComputeState();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -104,20 +104,11 @@ void TextDecoder::Decode(const Optional<ArrayBufferViewOrArrayBuffer>& aBuffer,
|
||||
DecodeNative(nullptr, aOptions.mStream, aOutDecodedString, aRv);
|
||||
return;
|
||||
}
|
||||
const ArrayBufferViewOrArrayBuffer& buf = aBuffer.Value();
|
||||
uint8_t* data;
|
||||
uint32_t length;
|
||||
if (buf.IsArrayBufferView()) {
|
||||
buf.GetAsArrayBufferView().ComputeState();
|
||||
data = buf.GetAsArrayBufferView().Data();
|
||||
length = buf.GetAsArrayBufferView().Length();
|
||||
} else {
|
||||
MOZ_ASSERT(buf.IsArrayBuffer());
|
||||
buf.GetAsArrayBuffer().ComputeState();
|
||||
data = buf.GetAsArrayBuffer().Data();
|
||||
length = buf.GetAsArrayBuffer().Length();
|
||||
}
|
||||
DecodeNative(Span(data, length), aOptions.mStream, aOutDecodedString, aRv);
|
||||
|
||||
ProcessTypedArrays(aBuffer.Value(), [&](const Span<uint8_t>& aData,
|
||||
JS::AutoCheckCannotGC&&) {
|
||||
DecodeNative(aData, aOptions.mStream, aOutDecodedString, aRv);
|
||||
});
|
||||
}
|
||||
|
||||
void TextDecoderCommon::GetEncoding(nsAString& aEncoding) {
|
||||
|
@ -46,28 +46,6 @@ JSObject* TextDecoderStream::WrapObject(JSContext* aCx,
|
||||
return TextDecoderStream_Binding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
// TODO: This does not allow shared array buffers, just as the non-stream
|
||||
// TextDecoder/Encoder don't. (Bug 1561594)
|
||||
Span<const uint8_t> ExtractSpanFromBufferSource(
|
||||
JSContext* aCx, JS::Handle<JS::Value> aBufferSource, ErrorResult& aRv) {
|
||||
RootedUnion<OwningArrayBufferViewOrArrayBuffer> bufferSource(aCx);
|
||||
if (!bufferSource.Init(aCx, aBufferSource)) {
|
||||
aRv.MightThrowJSException();
|
||||
aRv.StealExceptionFromJSContext(aCx);
|
||||
return Span<const uint8_t>();
|
||||
}
|
||||
|
||||
if (bufferSource.IsArrayBufferView()) {
|
||||
ArrayBufferView& view = bufferSource.GetAsArrayBufferView();
|
||||
view.ComputeState();
|
||||
return Span(view.Data(), view.Length());
|
||||
}
|
||||
|
||||
ArrayBuffer& buffer = bufferSource.GetAsArrayBuffer();
|
||||
buffer.ComputeState();
|
||||
return Span(buffer.Data(), buffer.Length());
|
||||
}
|
||||
|
||||
class TextDecoderStreamAlgorithms : public TransformerAlgorithmsWrapper {
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(TextDecoderStreamAlgorithms,
|
||||
@ -81,24 +59,22 @@ class TextDecoderStreamAlgorithms : public TransformerAlgorithmsWrapper {
|
||||
// Note that the most of the decoding algorithm is implemented in
|
||||
// mozilla::Decoder, and this is mainly about calling it properly.
|
||||
// https://encoding.spec.whatwg.org/#decode-and-enqueue-a-chunk
|
||||
MOZ_CAN_RUN_SCRIPT void DecodeSpanAndEnqueue(
|
||||
JSContext* aCx, Span<const uint8_t> aInput, bool aFlush,
|
||||
// TODO: This does not allow shared array buffers, just as the non-stream
|
||||
// TextDecoder/Encoder don't. (Bug 1561594)
|
||||
MOZ_CAN_RUN_SCRIPT void DecodeBufferSourceAndEnqueue(
|
||||
JSContext* aCx, OwningArrayBufferViewOrArrayBuffer* aInput, bool aFlush,
|
||||
TransformStreamDefaultController& aController, ErrorResult& aRv) {
|
||||
CheckedInt<nsAString::size_type> needed =
|
||||
mDecoderStream->Decoder()->MaxUTF16BufferLength(aInput.Length());
|
||||
if (!needed.isValid()) {
|
||||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
return;
|
||||
}
|
||||
|
||||
nsString outDecodedString;
|
||||
auto output = outDecodedString.GetMutableData(needed.value(), fallible);
|
||||
if (!output) {
|
||||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
return;
|
||||
if (aInput) {
|
||||
ProcessTypedArrays(*aInput, [&](const Span<const uint8_t>& aData,
|
||||
JS::AutoCheckCannotGC&&) {
|
||||
mDecoderStream->DecodeNative(aData, !aFlush, outDecodedString, aRv);
|
||||
});
|
||||
} else {
|
||||
mDecoderStream->DecodeNative(Span<const uint8_t>(), !aFlush,
|
||||
outDecodedString, aRv);
|
||||
}
|
||||
|
||||
mDecoderStream->DecodeNative(aInput, !aFlush, outDecodedString, aRv);
|
||||
if (aRv.Failed()) {
|
||||
return;
|
||||
}
|
||||
@ -107,7 +83,7 @@ class TextDecoderStreamAlgorithms : public TransformerAlgorithmsWrapper {
|
||||
// Step 4.2. If outputChunk is non-empty, then enqueue outputChunk in
|
||||
// decoder’s transform.
|
||||
JS::Rooted<JS::Value> outputChunk(aCx);
|
||||
if (!xpc::NonVoidStringToJsval(aCx, outDecodedString, &outputChunk)) {
|
||||
if (!ToJSValue(aCx, outDecodedString, &outputChunk)) {
|
||||
JS_ClearPendingException(aCx);
|
||||
aRv.Throw(NS_ERROR_UNEXPECTED);
|
||||
return;
|
||||
@ -136,13 +112,14 @@ class TextDecoderStreamAlgorithms : public TransformerAlgorithmsWrapper {
|
||||
|
||||
// Step 1. Let bufferSource be the result of converting chunk to an
|
||||
// [AllowShared] BufferSource.
|
||||
// (But here we get a mozilla::Span instead)
|
||||
Span<const uint8_t> input = ExtractSpanFromBufferSource(cx, aChunk, aRv);
|
||||
if (aRv.Failed()) {
|
||||
RootedUnion<OwningArrayBufferViewOrArrayBuffer> bufferSource(cx);
|
||||
if (!bufferSource.Init(cx, aChunk)) {
|
||||
aRv.MightThrowJSException();
|
||||
aRv.StealExceptionFromJSContext(cx);
|
||||
return;
|
||||
}
|
||||
|
||||
DecodeSpanAndEnqueue(cx, input, false, aController, aRv);
|
||||
DecodeBufferSourceAndEnqueue(cx, &bufferSource, false, aController, aRv);
|
||||
}
|
||||
|
||||
// https://encoding.spec.whatwg.org/#dom-textdecoderstream
|
||||
@ -162,7 +139,7 @@ class TextDecoderStreamAlgorithms : public TransformerAlgorithmsWrapper {
|
||||
// https://encoding.spec.whatwg.org/#flush-and-enqueue
|
||||
// (The flush and enqueue algorithm is basically a subset of decode and
|
||||
// enqueue one, so let's reuse it)
|
||||
DecodeSpanAndEnqueue(cx, Span<const uint8_t>(), true, aController, aRv);
|
||||
DecodeBufferSourceAndEnqueue(cx, nullptr, true, aController, aRv);
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -69,9 +69,6 @@ class TextDecoderStream final : public nsISupports,
|
||||
RefPtr<TransformStream> mStream;
|
||||
};
|
||||
|
||||
Span<const uint8_t> ExtractSpanFromBufferSource(
|
||||
JSContext* aCx, JS::Handle<JS::Value> aBufferSource, ErrorResult& aRv);
|
||||
|
||||
} // namespace mozilla::dom
|
||||
|
||||
#endif // DOM_ENCODING_TEXTDECODERSTREAM_H_
|
||||
|
@ -30,17 +30,21 @@ void TextEncoder::EncodeInto(JSContext* aCx, JS::Handle<JSString*> aSrc,
|
||||
const Uint8Array& aDst,
|
||||
TextEncoderEncodeIntoResult& aResult,
|
||||
OOMReporter& aError) {
|
||||
aDst.ComputeState();
|
||||
size_t read;
|
||||
size_t written;
|
||||
auto maybe = JS_EncodeStringToUTF8BufferPartial(
|
||||
aCx, aSrc, AsWritableChars(Span(aDst.Data(), aDst.Length())));
|
||||
DebugOnly<size_t> dstLength = 0;
|
||||
auto maybe = aDst.ProcessData(
|
||||
[&](const Span<uint8_t>& aData, JS::AutoCheckCannotGC&&) {
|
||||
dstLength = aData.Length();
|
||||
return JS_EncodeStringToUTF8BufferPartial(aCx, aSrc,
|
||||
AsWritableChars(aData));
|
||||
});
|
||||
if (!maybe) {
|
||||
aError.ReportOOM();
|
||||
return;
|
||||
}
|
||||
size_t read;
|
||||
size_t written;
|
||||
std::tie(read, written) = *maybe;
|
||||
MOZ_ASSERT(written <= aDst.Length());
|
||||
MOZ_ASSERT(written <= dstLength);
|
||||
aResult.mRead.Construct() = read;
|
||||
aResult.mWritten.Construct() = written;
|
||||
}
|
||||
|
@ -536,24 +536,18 @@ uint64_t FileSystemSyncAccessHandle::ReadOrWrite(
|
||||
return 0;
|
||||
};
|
||||
|
||||
const auto dataSpan = [&aBuffer]() {
|
||||
if (aBuffer.IsArrayBuffer()) {
|
||||
const ArrayBuffer& buffer = aBuffer.GetAsArrayBuffer();
|
||||
buffer.ComputeState();
|
||||
return Span{buffer.Data(), buffer.Length()};
|
||||
// Handle seek before read ('at')
|
||||
const auto at = [&aOptions]() -> uint64_t {
|
||||
if (aOptions.mAt.WasPassed()) {
|
||||
return aOptions.mAt.Value();
|
||||
}
|
||||
MOZ_ASSERT(aBuffer.IsArrayBufferView());
|
||||
const ArrayBufferView& buffer = aBuffer.GetAsArrayBufferView();
|
||||
buffer.ComputeState();
|
||||
return Span{buffer.Data(), buffer.Length()};
|
||||
// Spec says default for at is 0 (2.6)
|
||||
return 0;
|
||||
}();
|
||||
|
||||
CheckedInt<int64_t> offset = 0;
|
||||
if (aOptions.mAt.WasPassed()) {
|
||||
// Handle seek before read ('at')
|
||||
offset = CheckedInt<int64_t>(aOptions.mAt.Value());
|
||||
const auto offset = CheckedInt<int64_t>(at);
|
||||
QM_TRY(MOZ_TO_RESULT(offset.isValid()), throwAndReturn);
|
||||
}
|
||||
|
||||
AutoSyncLoopHolder syncLoop(mWorkerRef->Private(), Canceling);
|
||||
|
||||
nsCOMPtr<nsISerialEventTarget> syncLoopTarget =
|
||||
@ -565,13 +559,14 @@ uint64_t FileSystemSyncAccessHandle::ReadOrWrite(
|
||||
|
||||
uint64_t totalCount = 0;
|
||||
|
||||
ProcessTypedArraysFixed(aBuffer, [&](const Span<uint8_t> aData) {
|
||||
InvokeAsync(
|
||||
mIOTaskQueue, __func__,
|
||||
[selfHolder = fs::TargetPtrHolder(this), dataSpan,
|
||||
use_offset = aOptions.mAt.WasPassed(), offset, aRead, &totalCount]() {
|
||||
[selfHolder = fs::TargetPtrHolder(this), aData,
|
||||
use_offset = aOptions.mAt.WasPassed(), offset, aRead, syncLoopTarget,
|
||||
&totalCount]() {
|
||||
QM_TRY(MOZ_TO_RESULT(selfHolder->EnsureStream()),
|
||||
CreateAndRejectBoolPromise);
|
||||
|
||||
if (use_offset) {
|
||||
LOG_VERBOSE(("%p: Seeking to %" PRIu64, selfHolder->mStream.get(),
|
||||
offset.value()));
|
||||
@ -586,18 +581,17 @@ uint64_t FileSystemSyncAccessHandle::ReadOrWrite(
|
||||
|
||||
if (aRead) {
|
||||
LOG_VERBOSE(("%p: Reading %zu bytes", selfHolder->mStream.get(),
|
||||
dataSpan.Length()));
|
||||
aData.Length()));
|
||||
|
||||
inputStream = selfHolder->mStream->InputStream();
|
||||
|
||||
outputStream =
|
||||
FixedBufferOutputStream::Create(AsWritableChars(dataSpan));
|
||||
FixedBufferOutputStream::Create(AsWritableChars(aData));
|
||||
} else {
|
||||
LOG_VERBOSE(("%p: Writing %zu bytes", selfHolder->mStream.get(),
|
||||
dataSpan.Length()));
|
||||
aData.Length()));
|
||||
|
||||
QM_TRY(MOZ_TO_RESULT(NS_NewByteInputStream(
|
||||
getter_AddRefs(inputStream), AsChars(dataSpan),
|
||||
getter_AddRefs(inputStream), AsChars(aData),
|
||||
NS_ASSIGNMENT_DEPEND)),
|
||||
CreateAndRejectBoolPromise);
|
||||
|
||||
@ -631,6 +625,7 @@ uint64_t FileSystemSyncAccessHandle::ReadOrWrite(
|
||||
});
|
||||
|
||||
MOZ_ALWAYS_SUCCEEDS(syncLoop.Run());
|
||||
});
|
||||
|
||||
return totalCount;
|
||||
}
|
||||
|
@ -23,24 +23,6 @@ LogModule* GetEMEVerboseLog() {
|
||||
return log;
|
||||
}
|
||||
|
||||
ArrayData GetArrayBufferViewOrArrayBufferData(
|
||||
const dom::ArrayBufferViewOrArrayBuffer& aBufferOrView) {
|
||||
MOZ_ASSERT(aBufferOrView.IsArrayBuffer() ||
|
||||
aBufferOrView.IsArrayBufferView());
|
||||
JS::AutoCheckCannotGC nogc;
|
||||
if (aBufferOrView.IsArrayBuffer()) {
|
||||
const dom::ArrayBuffer& buffer = aBufferOrView.GetAsArrayBuffer();
|
||||
buffer.ComputeState();
|
||||
return ArrayData(buffer.Data(), buffer.Length());
|
||||
} else if (aBufferOrView.IsArrayBufferView()) {
|
||||
const dom::ArrayBufferView& bufferview =
|
||||
aBufferOrView.GetAsArrayBufferView();
|
||||
bufferview.ComputeState();
|
||||
return ArrayData(bufferview.Data(), bufferview.Length());
|
||||
}
|
||||
return ArrayData(nullptr, 0);
|
||||
}
|
||||
|
||||
void CopyArrayBufferViewOrArrayBufferData(
|
||||
const dom::ArrayBufferViewOrArrayBuffer& aBufferOrView,
|
||||
nsTArray<uint8_t>& aOutData) {
|
||||
|
@ -47,36 +47,6 @@ void CopyArrayBufferViewOrArrayBufferData(
|
||||
const dom::ArrayBufferViewOrArrayBuffer& aBufferOrView,
|
||||
nsTArray<uint8_t>& aOutData);
|
||||
|
||||
struct ArrayData {
|
||||
explicit ArrayData(const uint8_t* aData, size_t aLength)
|
||||
: mData(aData), mLength(aLength) {}
|
||||
const uint8_t* mData;
|
||||
const size_t mLength;
|
||||
bool IsValid() const { return mData != nullptr && mLength != 0; }
|
||||
bool operator==(const nsTArray<uint8_t>& aOther) const {
|
||||
return mLength == aOther.Length() &&
|
||||
memcmp(mData, aOther.Elements(), mLength) == 0;
|
||||
}
|
||||
};
|
||||
|
||||
// Helper function to extract data coming in from JS in an
|
||||
// (ArrayBuffer or ArrayBufferView) IDL typed function argument.
|
||||
//
|
||||
// Be *very* careful with this!
|
||||
//
|
||||
// Only use returned ArrayData inside the lifetime of the
|
||||
// ArrayBufferViewOrArrayBuffer; the ArrayData struct does not contain
|
||||
// a copy of the data!
|
||||
//
|
||||
// And do *not* call out to anything that could call into JavaScript,
|
||||
// while the ArrayData is live, as then all bets about the data not changing
|
||||
// are off! No calls into JS, no calls into JS-implemented WebIDL or XPIDL,
|
||||
// nothing. Beware!
|
||||
//
|
||||
// Only call this on a properly initialized ArrayBufferViewOrArrayBuffer.
|
||||
ArrayData GetArrayBufferViewOrArrayBufferData(
|
||||
const dom::ArrayBufferViewOrArrayBuffer& aBufferOrView);
|
||||
|
||||
nsString KeySystemToProxyName(const nsAString& aKeySystem);
|
||||
|
||||
bool IsClearkeyKeySystem(const nsAString& aKeySystem);
|
||||
|
@ -35,36 +35,36 @@ nsPIDOMWindowInner* MediaKeyStatusMap::GetParentObject() const {
|
||||
return mParent;
|
||||
}
|
||||
|
||||
const MediaKeyStatusMap::KeyStatus* MediaKeyStatusMap::FindKey(
|
||||
const ArrayBufferViewOrArrayBuffer& aKey) const {
|
||||
MOZ_ASSERT(aKey.IsArrayBuffer() || aKey.IsArrayBufferView());
|
||||
|
||||
return ProcessTypedArrays(aKey,
|
||||
[&](const Span<const uint8_t>& aData,
|
||||
JS::AutoCheckCannotGC&&) -> const KeyStatus* {
|
||||
for (const KeyStatus& status : mStatuses) {
|
||||
if (aData == Span(status.mKeyId)) {
|
||||
return &status;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
});
|
||||
}
|
||||
|
||||
void MediaKeyStatusMap::Get(const ArrayBufferViewOrArrayBuffer& aKey,
|
||||
OwningMediaKeyStatusOrUndefined& aOutValue,
|
||||
ErrorResult& aOutRv) const {
|
||||
ArrayData keyId = GetArrayBufferViewOrArrayBufferData(aKey);
|
||||
if (!keyId.IsValid()) {
|
||||
const KeyStatus* status = FindKey(aKey);
|
||||
if (!status) {
|
||||
aOutValue.SetUndefined();
|
||||
return;
|
||||
}
|
||||
for (const KeyStatus& status : mStatuses) {
|
||||
if (keyId == status.mKeyId) {
|
||||
aOutValue.SetAsMediaKeyStatus() = status.mStatus;
|
||||
return;
|
||||
}
|
||||
}
|
||||
aOutValue.SetUndefined();
|
||||
|
||||
aOutValue.SetAsMediaKeyStatus() = status->mStatus;
|
||||
}
|
||||
|
||||
bool MediaKeyStatusMap::Has(const ArrayBufferViewOrArrayBuffer& aKey) const {
|
||||
ArrayData keyId = GetArrayBufferViewOrArrayBufferData(aKey);
|
||||
if (!keyId.IsValid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const KeyStatus& status : mStatuses) {
|
||||
if (keyId == status.mKeyId) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return FindKey(aKey);
|
||||
}
|
||||
|
||||
uint32_t MediaKeyStatusMap::GetIterableLength() const {
|
||||
|
@ -83,6 +83,8 @@ class MediaKeyStatusMap final : public nsISupports, public nsWrapperCache {
|
||||
MediaKeyStatus mStatus;
|
||||
};
|
||||
|
||||
const KeyStatus* FindKey(const ArrayBufferViewOrArrayBuffer& aKey) const;
|
||||
|
||||
nsTArray<KeyStatus> mStatuses;
|
||||
};
|
||||
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/dom/MediaSourceBinding.h"
|
||||
#include "mozilla/dom/TimeRanges.h"
|
||||
#include "mozilla/dom/TypedArray.h"
|
||||
#include "nsError.h"
|
||||
#include "nsIRunnable.h"
|
||||
#include "nsThreadUtils.h"
|
||||
@ -178,18 +179,24 @@ void SourceBuffer::SetAppendWindowEnd(double aAppendWindowEnd,
|
||||
void SourceBuffer::AppendBuffer(const ArrayBuffer& aData, ErrorResult& aRv) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MSE_API("AppendBuffer(ArrayBuffer)");
|
||||
aData.ComputeState();
|
||||
DDLOG(DDLogCategory::API, "AppendBuffer", aData.Length());
|
||||
AppendData(aData.Data(), aData.Length(), aRv);
|
||||
RefPtr<MediaByteBuffer> data = PrepareAppend(aData, aRv);
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
DDLOG(DDLogCategory::API, "AppendBuffer", uint64_t(data->Length()));
|
||||
AppendData(std::move(data), aRv);
|
||||
}
|
||||
|
||||
void SourceBuffer::AppendBuffer(const ArrayBufferView& aData,
|
||||
ErrorResult& aRv) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MSE_API("AppendBuffer(ArrayBufferView)");
|
||||
aData.ComputeState();
|
||||
DDLOG(DDLogCategory::API, "AppendBuffer", aData.Length());
|
||||
AppendData(aData.Data(), aData.Length(), aRv);
|
||||
RefPtr<MediaByteBuffer> data = PrepareAppend(aData, aRv);
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
DDLOG(DDLogCategory::API, "AppendBuffer", uint64_t(data->Length()));
|
||||
AppendData(std::move(data), aRv);
|
||||
}
|
||||
|
||||
already_AddRefed<Promise> SourceBuffer::AppendBufferAsync(
|
||||
@ -197,10 +204,13 @@ already_AddRefed<Promise> SourceBuffer::AppendBufferAsync(
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
MSE_API("AppendBufferAsync(ArrayBuffer)");
|
||||
aData.ComputeState();
|
||||
DDLOG(DDLogCategory::API, "AppendBufferAsync", aData.Length());
|
||||
RefPtr<MediaByteBuffer> data = PrepareAppend(aData, aRv);
|
||||
if (!data) {
|
||||
return nullptr;
|
||||
}
|
||||
DDLOG(DDLogCategory::API, "AppendBufferAsync", uint64_t(data->Length()));
|
||||
|
||||
return AppendDataAsync(aData.Data(), aData.Length(), aRv);
|
||||
return AppendDataAsync(std::move(data), aRv);
|
||||
}
|
||||
|
||||
already_AddRefed<Promise> SourceBuffer::AppendBufferAsync(
|
||||
@ -208,10 +218,13 @@ already_AddRefed<Promise> SourceBuffer::AppendBufferAsync(
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
MSE_API("AppendBufferAsync(ArrayBufferView)");
|
||||
aData.ComputeState();
|
||||
DDLOG(DDLogCategory::API, "AppendBufferAsync", aData.Length());
|
||||
RefPtr<MediaByteBuffer> data = PrepareAppend(aData, aRv);
|
||||
if (!data) {
|
||||
return nullptr;
|
||||
}
|
||||
DDLOG(DDLogCategory::API, "AppendBufferAsync", uint64_t(data->Length()));
|
||||
|
||||
return AppendDataAsync(aData.Data(), aData.Length(), aRv);
|
||||
return AppendDataAsync(std::move(data), aRv);
|
||||
}
|
||||
|
||||
void SourceBuffer::Abort(ErrorResult& aRv) {
|
||||
@ -546,27 +559,22 @@ void SourceBuffer::CheckEndTime() {
|
||||
}
|
||||
}
|
||||
|
||||
void SourceBuffer::AppendData(const uint8_t* aData, uint32_t aLength,
|
||||
void SourceBuffer::AppendData(RefPtr<MediaByteBuffer>&& aData,
|
||||
ErrorResult& aRv) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MSE_DEBUG("AppendData(aLength=%u)", aLength);
|
||||
MSE_DEBUG("AppendData(aLength=%zu)", aData->Length());
|
||||
|
||||
RefPtr<MediaByteBuffer> data = PrepareAppend(aData, aLength, aRv);
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
StartUpdating();
|
||||
|
||||
mTrackBuffersManager->AppendData(data.forget(), mCurrentAttributes)
|
||||
mTrackBuffersManager->AppendData(aData.forget(), mCurrentAttributes)
|
||||
->Then(mAbstractMainThread, __func__, this,
|
||||
&SourceBuffer::AppendDataCompletedWithSuccess,
|
||||
&SourceBuffer::AppendDataErrored)
|
||||
->Track(mPendingAppend);
|
||||
}
|
||||
|
||||
already_AddRefed<Promise> SourceBuffer::AppendDataAsync(const uint8_t* aData,
|
||||
uint32_t aLength,
|
||||
ErrorResult& aRv) {
|
||||
already_AddRefed<Promise> SourceBuffer::AppendDataAsync(
|
||||
RefPtr<MediaByteBuffer>&& aData, ErrorResult& aRv) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (!IsAttached()) {
|
||||
@ -586,7 +594,7 @@ already_AddRefed<Promise> SourceBuffer::AppendDataAsync(const uint8_t* aData,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AppendData(aData, aLength, aRv);
|
||||
AppendData(std::move(aData), aRv);
|
||||
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
@ -718,6 +726,13 @@ already_AddRefed<MediaByteBuffer> SourceBuffer::PrepareAppend(
|
||||
return data.forget();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
already_AddRefed<MediaByteBuffer> SourceBuffer::PrepareAppend(
|
||||
const T& aData, ErrorResult& aRv) {
|
||||
return aData.ProcessFixedData([&](const Span<uint8_t>& aData) {
|
||||
return PrepareAppend(aData.Elements(), aData.Length(), aRv);
|
||||
});
|
||||
}
|
||||
TimeUnit SourceBuffer::GetBufferedEnd() {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
ErrorResult dummy;
|
||||
|
@ -151,10 +151,10 @@ class SourceBuffer final : public DOMEventTargetHelper,
|
||||
void CheckEndTime();
|
||||
|
||||
// Shared implementation of AppendBuffer overloads.
|
||||
void AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aRv);
|
||||
void AppendData(RefPtr<MediaByteBuffer>&& aData, ErrorResult& aRv);
|
||||
// Shared implementation of AppendBufferAsync overloads.
|
||||
already_AddRefed<Promise> AppendDataAsync(const uint8_t* aData,
|
||||
uint32_t aLength, ErrorResult& aRv);
|
||||
already_AddRefed<Promise> AppendDataAsync(RefPtr<MediaByteBuffer>&& aData,
|
||||
ErrorResult& aRv);
|
||||
|
||||
void PrepareRemove(double aStart, double aEnd, ErrorResult& aRv);
|
||||
|
||||
@ -169,6 +169,9 @@ class SourceBuffer final : public DOMEventTargetHelper,
|
||||
already_AddRefed<MediaByteBuffer> PrepareAppend(const uint8_t* aData,
|
||||
uint32_t aLength,
|
||||
ErrorResult& aRv);
|
||||
template <typename T>
|
||||
already_AddRefed<MediaByteBuffer> PrepareAppend(const T& aData,
|
||||
ErrorResult& aRv);
|
||||
|
||||
void AppendDataCompletedWithSuccess(
|
||||
const SourceBufferTask::AppendBufferResult& aResult);
|
||||
|
@ -215,15 +215,14 @@ void AnalyserNode::GetFloatFrequencyData(const Float32Array& aArray) {
|
||||
return;
|
||||
}
|
||||
|
||||
aArray.ComputeState();
|
||||
|
||||
float* buffer = aArray.Data();
|
||||
size_t length = std::min(size_t(aArray.Length()), mOutputBuffer.Length());
|
||||
aArray.ProcessData([&](const Span<float>& aData, JS::AutoCheckCannotGC&&) {
|
||||
size_t length = std::min(size_t(aData.Length()), mOutputBuffer.Length());
|
||||
|
||||
for (size_t i = 0; i < length; ++i) {
|
||||
buffer[i] = WebAudioUtils::ConvertLinearToDecibels(
|
||||
aData[i] = WebAudioUtils::ConvertLinearToDecibels(
|
||||
mOutputBuffer[i], -std::numeric_limits<float>::infinity());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void AnalyserNode::GetByteFrequencyData(const Uint8Array& aArray) {
|
||||
@ -234,35 +233,33 @@ void AnalyserNode::GetByteFrequencyData(const Uint8Array& aArray) {
|
||||
|
||||
const double rangeScaleFactor = 1.0 / (mMaxDecibels - mMinDecibels);
|
||||
|
||||
aArray.ComputeState();
|
||||
|
||||
unsigned char* buffer = aArray.Data();
|
||||
size_t length = std::min(size_t(aArray.Length()), mOutputBuffer.Length());
|
||||
aArray.ProcessData([&](const Span<uint8_t>& aData, JS::AutoCheckCannotGC&&) {
|
||||
size_t length = std::min(size_t(aData.Length()), mOutputBuffer.Length());
|
||||
|
||||
for (size_t i = 0; i < length; ++i) {
|
||||
const double decibels =
|
||||
WebAudioUtils::ConvertLinearToDecibels(mOutputBuffer[i], mMinDecibels);
|
||||
const double decibels = WebAudioUtils::ConvertLinearToDecibels(
|
||||
mOutputBuffer[i], mMinDecibels);
|
||||
// scale down the value to the range of [0, UCHAR_MAX]
|
||||
const double scaled = std::max(
|
||||
0.0, std::min(double(UCHAR_MAX), UCHAR_MAX * (decibels - mMinDecibels) *
|
||||
rangeScaleFactor));
|
||||
buffer[i] = static_cast<unsigned char>(scaled);
|
||||
0.0,
|
||||
std::min(double(UCHAR_MAX),
|
||||
UCHAR_MAX * (decibels - mMinDecibels) * rangeScaleFactor));
|
||||
aData[i] = static_cast<unsigned char>(scaled);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void AnalyserNode::GetFloatTimeDomainData(const Float32Array& aArray) {
|
||||
aArray.ComputeState();
|
||||
aArray.ProcessData([&](const Span<float>& aData, JS::AutoCheckCannotGC&&) {
|
||||
size_t length = std::min(aData.Length(), size_t(FftSize()));
|
||||
|
||||
float* buffer = aArray.Data();
|
||||
size_t length = std::min(aArray.Length(), FftSize());
|
||||
|
||||
GetTimeDomainData(buffer, length);
|
||||
GetTimeDomainData(aData.Elements(), length);
|
||||
});
|
||||
}
|
||||
|
||||
void AnalyserNode::GetByteTimeDomainData(const Uint8Array& aArray) {
|
||||
aArray.ComputeState();
|
||||
|
||||
size_t length = std::min(aArray.Length(), FftSize());
|
||||
aArray.ProcessData([&](const Span<uint8_t>& aData, JS::AutoCheckCannotGC&&) {
|
||||
size_t length = std::min(aData.Length(), size_t(FftSize()));
|
||||
|
||||
AlignedTArray<float> tmpBuffer;
|
||||
if (!tmpBuffer.SetLength(length, fallible)) {
|
||||
@ -271,7 +268,7 @@ void AnalyserNode::GetByteTimeDomainData(const Uint8Array& aArray) {
|
||||
|
||||
GetTimeDomainData(tmpBuffer.Elements(), length);
|
||||
|
||||
unsigned char* buffer = aArray.Data();
|
||||
unsigned char* buffer = aData.Elements();
|
||||
for (size_t i = 0; i < length; ++i) {
|
||||
const float value = tmpBuffer[i];
|
||||
// scale the value to the range of [0, UCHAR_MAX]
|
||||
@ -279,6 +276,7 @@ void AnalyserNode::GetByteTimeDomainData(const Uint8Array& aArray) {
|
||||
std::max(0.0f, std::min(float(UCHAR_MAX), 128.0f * (value + 1.0f)));
|
||||
buffer[i] = static_cast<unsigned char>(scaled);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
bool AnalyserNode::FFTAnalysis() {
|
||||
|
@ -323,8 +323,10 @@ void AudioBuffer::CopyFromChannel(const Float32Array& aDestination,
|
||||
return;
|
||||
}
|
||||
JS::AutoCheckCannotGC nogc;
|
||||
aDestination.ComputeState();
|
||||
uint32_t count = std::min(length - aBufferOffset, aDestination.Length());
|
||||
MOZ_RELEASE_ASSERT(!JS_GetTypedArraySharedness(aDestination.Obj()));
|
||||
auto calculateCount = [=](uint32_t aLength) -> uint32_t {
|
||||
return std::min(length - aBufferOffset, aLength);
|
||||
};
|
||||
|
||||
JSObject* channelArray = mJSChannels[aChannelNumber];
|
||||
if (channelArray) {
|
||||
@ -338,17 +340,27 @@ void AudioBuffer::CopyFromChannel(const Float32Array& aDestination,
|
||||
// The sourceData arrays should all have originated in
|
||||
// RestoreJSChannelData, where they are created unshared.
|
||||
MOZ_ASSERT(!isShared);
|
||||
PodMove(aDestination.Data(), sourceData + aBufferOffset, count);
|
||||
aDestination.ProcessData(
|
||||
[&](const Span<float>& aData, JS::AutoCheckCannotGC&&) {
|
||||
PodMove(aData.Elements(), sourceData + aBufferOffset,
|
||||
calculateCount(aData.Length()));
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mSharedChannels.IsNull()) {
|
||||
aDestination.ProcessData([&](const Span<float>& aData,
|
||||
JS::AutoCheckCannotGC&&) {
|
||||
CopyChannelDataToFloat(mSharedChannels, aChannelNumber, aBufferOffset,
|
||||
aDestination.Data(), count);
|
||||
aData.Elements(), calculateCount(aData.Length()));
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
PodZero(aDestination.Data(), count);
|
||||
aDestination.ProcessData(
|
||||
[&](const Span<float>& aData, JS::AutoCheckCannotGC&&) {
|
||||
PodZero(aData.Elements(), calculateCount(aData.Length()));
|
||||
});
|
||||
}
|
||||
|
||||
void AudioBuffer::CopyToChannel(JSContext* aJSContext,
|
||||
@ -374,14 +386,20 @@ void AudioBuffer::CopyToChannel(JSContext* aJSContext,
|
||||
return;
|
||||
}
|
||||
|
||||
aSource.ComputeState();
|
||||
uint32_t count = std::min(length - aBufferOffset, aSource.Length());
|
||||
int64_t offset = aBufferOffset;
|
||||
aSource.ProcessData([&](const Span<float>& aData, JS::AutoCheckCannotGC&&) {
|
||||
MOZ_ASSERT_IF(std::numeric_limits<decltype(aData.Length())>::max() >
|
||||
std::numeric_limits<int64_t>::max(),
|
||||
aData.Length() <= std::numeric_limits<int64_t>::max());
|
||||
int64_t srcLength = int64_t(aData.Length());
|
||||
size_t count = std::max(int64_t(0), std::min(length - offset, srcLength));
|
||||
bool isShared = false;
|
||||
float* channelData = JS_GetFloat32ArrayData(channelArray, &isShared, nogc);
|
||||
// The channelData arrays should all have originated in
|
||||
// RestoreJSChannelData, where they are created unshared.
|
||||
MOZ_ASSERT(!isShared);
|
||||
PodMove(channelData + aBufferOffset, aSource.Data(), count);
|
||||
PodMove(channelData + aBufferOffset, aData.Elements(), count);
|
||||
});
|
||||
}
|
||||
|
||||
void AudioBuffer::GetChannelData(JSContext* aJSContext, uint32_t aChannel,
|
||||
|
@ -659,19 +659,18 @@ already_AddRefed<Promise> AudioContext::DecodeAudioData(
|
||||
}
|
||||
|
||||
JSAutoRealm ar(cx, obj);
|
||||
aBuffer.ComputeState();
|
||||
|
||||
if (!aBuffer.Data()) {
|
||||
// Detach the array buffer
|
||||
size_t length = JS::GetArrayBufferByteLength(obj);
|
||||
uint8_t* data = static_cast<uint8_t*>(JS::StealArrayBufferContents(cx, obj));
|
||||
if (!data) {
|
||||
JS_ClearPendingException(cx);
|
||||
|
||||
// Throw if the buffer is detached
|
||||
aRv.ThrowTypeError("Buffer argument can't be a detached buffer");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Detach the array buffer
|
||||
size_t length = aBuffer.Length();
|
||||
|
||||
uint8_t* data = static_cast<uint8_t*>(JS::StealArrayBufferContents(cx, obj));
|
||||
|
||||
// Sniff the content of the media.
|
||||
// Failed type sniffing will be handled by AsyncDecodeWebAudio.
|
||||
nsAutoCString contentType;
|
||||
|
@ -5,6 +5,7 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "BiquadFilterNode.h"
|
||||
#include <algorithm>
|
||||
#include "AlignmentUtils.h"
|
||||
#include "AudioNodeEngine.h"
|
||||
#include "AudioNodeTrack.h"
|
||||
@ -304,35 +305,40 @@ void BiquadFilterNode::GetFrequencyResponse(const Float32Array& aFrequencyHz,
|
||||
const Float32Array& aMagResponse,
|
||||
const Float32Array& aPhaseResponse,
|
||||
ErrorResult& aRv) {
|
||||
aFrequencyHz.ComputeState();
|
||||
aMagResponse.ComputeState();
|
||||
aPhaseResponse.ComputeState();
|
||||
|
||||
if (!(aFrequencyHz.Length() == aMagResponse.Length() &&
|
||||
aMagResponse.Length() == aPhaseResponse.Length())) {
|
||||
UniquePtr<float[]> frequencies;
|
||||
size_t length;
|
||||
const double currentTime = Context()->CurrentTime();
|
||||
aFrequencyHz.ProcessData([&](const Span<float>& aFrequencyData,
|
||||
JS::AutoCheckCannotGC&&) {
|
||||
aMagResponse.ProcessData([&](const Span<float>& aMagData,
|
||||
JS::AutoCheckCannotGC&&) {
|
||||
aPhaseResponse.ProcessData([&](const Span<float>& aPhaseData,
|
||||
JS::AutoCheckCannotGC&&) {
|
||||
length = aFrequencyData.Length();
|
||||
if (length != aMagData.Length() || length != aPhaseData.Length()) {
|
||||
aRv.ThrowInvalidAccessError("Parameter lengths must match");
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t length = aFrequencyHz.Length();
|
||||
if (!length) {
|
||||
if (length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
frequencies = MakeUniqueForOverwriteFallible<float[]>(length);
|
||||
if (!frequencies) {
|
||||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
return;
|
||||
}
|
||||
|
||||
auto frequencies = MakeUnique<float[]>(length);
|
||||
float* frequencyHz = aFrequencyHz.Data();
|
||||
const double nyquist = Context()->SampleRate() * 0.5;
|
||||
|
||||
// Normalize the frequencies
|
||||
for (uint32_t i = 0; i < length; ++i) {
|
||||
if (frequencyHz[i] >= 0 && frequencyHz[i] <= nyquist) {
|
||||
frequencies[i] = static_cast<float>(frequencyHz[i] / nyquist);
|
||||
} else {
|
||||
frequencies[i] = std::numeric_limits<float>::quiet_NaN();
|
||||
}
|
||||
std::transform(aFrequencyData.begin(), aFrequencyData.end(),
|
||||
frequencies.get(), [&](float aFrequency) {
|
||||
if (aFrequency >= 0 && aFrequency <= nyquist) {
|
||||
return static_cast<float>(aFrequency / nyquist);
|
||||
}
|
||||
|
||||
const double currentTime = Context()->CurrentTime();
|
||||
return std::numeric_limits<float>::quiet_NaN();
|
||||
});
|
||||
|
||||
double freq = mFrequency->GetValueAtTime(currentTime);
|
||||
double q = mQ->GetValueAtTime(currentTime);
|
||||
@ -343,7 +349,10 @@ void BiquadFilterNode::GetFrequencyResponse(const Float32Array& aFrequencyHz,
|
||||
SetParamsOnBiquad(biquad, Context()->SampleRate(), mType, freq, q, gain,
|
||||
detune);
|
||||
biquad.getFrequencyResponse(int(length), frequencies.get(),
|
||||
aMagResponse.Data(), aPhaseResponse.Data());
|
||||
aMagData.Elements(), aPhaseData.Elements());
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace mozilla::dom
|
||||
|
@ -226,33 +226,41 @@ JSObject* IIRFilterNode::WrapObject(JSContext* aCx,
|
||||
void IIRFilterNode::GetFrequencyResponse(const Float32Array& aFrequencyHz,
|
||||
const Float32Array& aMagResponse,
|
||||
const Float32Array& aPhaseResponse) {
|
||||
aFrequencyHz.ComputeState();
|
||||
aMagResponse.ComputeState();
|
||||
aPhaseResponse.ComputeState();
|
||||
|
||||
uint32_t length =
|
||||
std::min(std::min(aFrequencyHz.Length(), aMagResponse.Length()),
|
||||
aPhaseResponse.Length());
|
||||
aFrequencyHz.ProcessData([&](const Span<float>& aFrequencyData,
|
||||
JS::AutoCheckCannotGC&&) {
|
||||
aMagResponse.ProcessData([&](const Span<float>& aMagData,
|
||||
JS::AutoCheckCannotGC&&) {
|
||||
aPhaseResponse.ProcessData([&](const Span<float>& aPhaseData,
|
||||
JS::AutoCheckCannotGC&&) {
|
||||
uint32_t length = std::min(
|
||||
{aFrequencyData.Length(), aMagData.Length(), aPhaseData.Length()});
|
||||
if (!length) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto frequencies = MakeUnique<float[]>(length);
|
||||
float* frequencyHz = aFrequencyHz.Data();
|
||||
auto frequencies = MakeUniqueForOverwriteFallible<float[]>(length);
|
||||
if (!frequencies) {
|
||||
return;
|
||||
}
|
||||
|
||||
const double nyquist = Context()->SampleRate() * 0.5;
|
||||
|
||||
// Normalize the frequencies
|
||||
for (uint32_t i = 0; i < length; ++i) {
|
||||
if (frequencyHz[i] >= 0 && frequencyHz[i] <= nyquist) {
|
||||
frequencies[i] = static_cast<float>(frequencyHz[i] / nyquist);
|
||||
} else {
|
||||
frequencies[i] = std::numeric_limits<float>::quiet_NaN();
|
||||
}
|
||||
std::transform(aFrequencyData.begin(), aFrequencyData.begin() + length,
|
||||
frequencies.get(), [&](float aFrequency) {
|
||||
if (aFrequency >= 0 && aFrequency <= nyquist) {
|
||||
return static_cast<float>(aFrequency / nyquist);
|
||||
}
|
||||
|
||||
return std::numeric_limits<float>::quiet_NaN();
|
||||
});
|
||||
|
||||
blink::IIRFilter filter(&mFeedforward, &mFeedback);
|
||||
filter.getFrequencyResponse(int(length), frequencies.get(),
|
||||
aMagResponse.Data(), aPhaseResponse.Data());
|
||||
aMagData.Elements(), aPhaseData.Elements());
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace mozilla::dom
|
||||
|
@ -144,32 +144,30 @@ already_AddRefed<EncodedVideoChunk> EncodedVideoChunk::Constructor(
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto r = GetSharedArrayBufferData(aInit.mData);
|
||||
if (r.isErr()) {
|
||||
aRv.Throw(r.unwrapErr());
|
||||
return nullptr;
|
||||
}
|
||||
Span<uint8_t> buf = r.unwrap();
|
||||
|
||||
auto buffer = ProcessTypedArrays(
|
||||
aInit.mData,
|
||||
[&](const Span<uint8_t>& aData,
|
||||
JS::AutoCheckCannotGC&&) -> RefPtr<MediaAlignedByteBuffer> {
|
||||
// Make sure it's in uint32_t's range.
|
||||
CheckedUint32 byteLength(buf.size_bytes());
|
||||
CheckedUint32 byteLength(aData.Length());
|
||||
if (!byteLength.isValid()) {
|
||||
aRv.Throw(NS_ERROR_INVALID_ARG);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (buf.size_bytes() == 0) {
|
||||
if (aData.Length() == 0) {
|
||||
LOGW("Buffer for constructing EncodedVideoChunk is empty!");
|
||||
}
|
||||
RefPtr<MediaAlignedByteBuffer> buf = MakeRefPtr<MediaAlignedByteBuffer>(
|
||||
aData.Elements(), aData.Length());
|
||||
|
||||
auto buffer =
|
||||
MakeRefPtr<MediaAlignedByteBuffer>(buf.data(), buf.size_bytes());
|
||||
// Instead of checking *buffer, size comparision is used to allow constructing
|
||||
// a zero-sized EncodedVideoChunk.
|
||||
if (!buffer || buffer->Size() != buf.size_bytes()) {
|
||||
// Instead of checking *buf, size comparision is used to allow
|
||||
// constructing a zero-sized EncodedVideoChunk.
|
||||
if (!buf || buf->Size() != aData.Length()) {
|
||||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
return nullptr;
|
||||
}
|
||||
return buf;
|
||||
});
|
||||
|
||||
RefPtr<EncodedVideoChunk> chunk(new EncodedVideoChunk(
|
||||
global, buffer.forget(), aInit.mType, aInit.mTimestamp,
|
||||
@ -207,20 +205,15 @@ void EncodedVideoChunk::CopyTo(
|
||||
ErrorResult& aRv) {
|
||||
AssertIsOnOwningThread();
|
||||
|
||||
auto r = GetSharedArrayBufferData(aDestination);
|
||||
if (r.isErr()) {
|
||||
aRv.Throw(r.unwrapErr());
|
||||
return;
|
||||
}
|
||||
Span<uint8_t> buf = r.unwrap();
|
||||
|
||||
if (mBuffer->Size() > buf.size_bytes()) {
|
||||
ProcessTypedArraysFixed(aDestination, [&](const Span<uint8_t>& aData) {
|
||||
if (mBuffer->Size() > aData.size_bytes()) {
|
||||
aRv.ThrowTypeError(
|
||||
"Destination ArrayBuffer smaller than source EncodedVideoChunk");
|
||||
return;
|
||||
}
|
||||
|
||||
PodCopy(buf.data(), mBuffer->Data(), mBuffer->Size());
|
||||
PodCopy(aData.data(), mBuffer->Data(), mBuffer->Size());
|
||||
});
|
||||
}
|
||||
|
||||
// https://w3c.github.io/webcodecs/#ref-for-deserialization-steps%E2%91%A0
|
||||
|
@ -906,39 +906,40 @@ static Result<RefPtr<VideoFrame>, nsCString> CreateVideoFrameFromBuffer(
|
||||
MOZ_TRY_VAR(combinedLayout,
|
||||
ComputeLayoutAndAllocationSize(parsedRect, format, optLayout));
|
||||
|
||||
Span<uint8_t> buffer;
|
||||
MOZ_TRY_VAR(buffer, GetArrayBufferData(aBuffer).mapErr([](nsresult aError) {
|
||||
return nsPrintfCString("Failed to get buffer data: %x",
|
||||
static_cast<uint32_t>(aError));
|
||||
}));
|
||||
|
||||
if (buffer.size_bytes() <
|
||||
static_cast<size_t>(combinedLayout.mAllocationSize)) {
|
||||
return Err(nsCString("data is too small"));
|
||||
}
|
||||
|
||||
// TODO: If codedSize is (3, 3) and visibleRect is (0, 0, 1, 1) but the data
|
||||
// is 2 x 2 RGBA buffer (2 x 2 x 4 bytes), it pass the above check. In this
|
||||
// case, we can crop it to a 1 x 1-codedSize image (Bug 1782128).
|
||||
if (buffer.size_bytes() < format.SampleCount(codedSize)) { // 1 byte/sample
|
||||
return Err(nsCString("data is too small"));
|
||||
}
|
||||
|
||||
// By spec, we should set visible* here. But if we don't change the image,
|
||||
// visible* is same as parsedRect here. The display{Width, Height} is
|
||||
// visible{Width, Height} if it's not set.
|
||||
|
||||
Maybe<uint64_t> duration = OptionalToMaybe(aInit.mDuration);
|
||||
|
||||
VideoColorSpaceInit colorSpace =
|
||||
PickColorSpace(OptionalToPointer(aInit.mColorSpace), aInit.mFormat);
|
||||
|
||||
RefPtr<layers::Image> data;
|
||||
MOZ_TRY_VAR(data,
|
||||
CreateImageFromBuffer(format, colorSpace, codedSize, buffer));
|
||||
MOZ_TRY_VAR(
|
||||
data,
|
||||
aBuffer.ProcessFixedData([&](const Span<uint8_t>& aData)
|
||||
-> Result<RefPtr<layers::Image>, nsCString> {
|
||||
if (aData.Length() <
|
||||
static_cast<size_t>(combinedLayout.mAllocationSize)) {
|
||||
return Err(nsCString("data is too small"));
|
||||
}
|
||||
|
||||
// TODO: If codedSize is (3, 3) and visibleRect is (0, 0, 1, 1) but the
|
||||
// data is 2 x 2 RGBA buffer (2 x 2 x 4 bytes), it pass the above check.
|
||||
// In this case, we can crop it to a 1 x 1-codedSize image (Bug
|
||||
// 1782128).
|
||||
if (aData.Length() < format.SampleCount(codedSize)) { // 1 byte/sample
|
||||
return Err(nsCString("data is too small"));
|
||||
}
|
||||
|
||||
return CreateImageFromBuffer(format, colorSpace, codedSize,
|
||||
Span(aData.Elements(), aData.Length()));
|
||||
}));
|
||||
|
||||
MOZ_ASSERT(data);
|
||||
MOZ_ASSERT(data->GetSize() == codedSize);
|
||||
|
||||
// By spec, we should set visible* here. But if we don't change the image,
|
||||
// visible* is same as parsedRect here. The display{Width, Height} is
|
||||
// visible{Width, Height} if it's not set.
|
||||
|
||||
// TODO: Spec should assign aInit.mFormat to inner format value:
|
||||
// https://github.com/w3c/webcodecs/issues/509.
|
||||
// This comment should be removed once the issue is resolved.
|
||||
@ -1660,14 +1661,8 @@ already_AddRefed<Promise> VideoFrame::CopyTo(
|
||||
}
|
||||
layout = r1.unwrap();
|
||||
|
||||
auto r2 = GetSharedArrayBufferData(aDestination);
|
||||
if (r2.isErr()) {
|
||||
p->MaybeRejectWithTypeError("Failed to get buffer");
|
||||
return p.forget();
|
||||
}
|
||||
Span<uint8_t> buffer = r2.unwrap();
|
||||
|
||||
if (buffer.size_bytes() < layout.mAllocationSize) {
|
||||
return ProcessTypedArraysFixed(aDestination, [&](const Span<uint8_t>& aData) {
|
||||
if (aData.size_bytes() < layout.mAllocationSize) {
|
||||
p->MaybeRejectWithTypeError("Destination buffer is too small");
|
||||
return p.forget();
|
||||
}
|
||||
@ -1677,8 +1672,8 @@ already_AddRefed<Promise> VideoFrame::CopyTo(
|
||||
nsTArray<Format::Plane> planes = mResource->mFormat->Planes();
|
||||
MOZ_ASSERT(layout.mComputedLayouts.Length() == planes.Length());
|
||||
|
||||
// TODO: These jobs can be run in a thread pool (bug 1780656) to unblock the
|
||||
// current thread.
|
||||
// TODO: These jobs can be run in a thread pool (bug 1780656) to unblock
|
||||
// the current thread.
|
||||
for (size_t i = 0; i < layout.mComputedLayouts.Length(); ++i) {
|
||||
ComputedPlaneLayout& l = layout.mComputedLayouts[i];
|
||||
uint32_t destinationOffset = l.mDestinationOffset;
|
||||
@ -1700,7 +1695,7 @@ already_AddRefed<Promise> VideoFrame::CopyTo(
|
||||
l.mSourceWidthBytes / mResource->mFormat->SampleBytes(planes[i]),
|
||||
l.mSourceHeight);
|
||||
if (!mResource->CopyTo(planes[i], {origin, size},
|
||||
buffer.From(destinationOffset),
|
||||
aData.From(destinationOffset),
|
||||
static_cast<size_t>(l.mDestinationStride))) {
|
||||
p->MaybeRejectWithTypeError(
|
||||
nsPrintfCString("Failed to copy image data in %s plane",
|
||||
@ -1711,10 +1706,11 @@ already_AddRefed<Promise> VideoFrame::CopyTo(
|
||||
|
||||
MOZ_ASSERT(layout.mComputedLayouts.Length() == planes.Length());
|
||||
// TODO: Spec doesn't resolve with a value. See
|
||||
// https://github.com/w3c/webcodecs/issues/510 This comment should be removed
|
||||
// once the issue is resolved.
|
||||
// https://github.com/w3c/webcodecs/issues/510 This comment should be
|
||||
// removed once the issue is resolved.
|
||||
p->MaybeResolve(planeLayouts);
|
||||
return p.forget();
|
||||
});
|
||||
}
|
||||
|
||||
// https://w3c.github.io/webcodecs/#dom-videoframe-clone
|
||||
|
@ -46,40 +46,6 @@ nsTArray<nsCString> GuessContainers(const nsAString& aCodec) {
|
||||
* The below are helpers to operate ArrayBuffer or ArrayBufferView.
|
||||
*/
|
||||
|
||||
template <class T>
|
||||
Result<Span<uint8_t>, nsresult> GetArrayBufferData(const T& aBuffer) {
|
||||
// Get buffer's data and length before using it.
|
||||
aBuffer.ComputeState();
|
||||
|
||||
CheckedInt<size_t> byteLength(sizeof(typename T::element_type));
|
||||
byteLength *= aBuffer.Length();
|
||||
if (NS_WARN_IF(!byteLength.isValid())) {
|
||||
return Err(NS_ERROR_INVALID_ARG);
|
||||
}
|
||||
|
||||
return Span<uint8_t>(aBuffer.Data(), byteLength.value());
|
||||
}
|
||||
|
||||
Result<Span<uint8_t>, nsresult> GetSharedArrayBufferData(
|
||||
const MaybeSharedArrayBufferViewOrMaybeSharedArrayBuffer& aBuffer) {
|
||||
if (aBuffer.IsArrayBufferView()) {
|
||||
return GetArrayBufferData(aBuffer.GetAsArrayBufferView());
|
||||
}
|
||||
|
||||
MOZ_ASSERT(aBuffer.IsArrayBuffer());
|
||||
return GetArrayBufferData(aBuffer.GetAsArrayBuffer());
|
||||
}
|
||||
|
||||
Result<Span<uint8_t>, nsresult> GetSharedArrayBufferData(
|
||||
const OwningMaybeSharedArrayBufferViewOrMaybeSharedArrayBuffer& aBuffer) {
|
||||
if (aBuffer.IsArrayBufferView()) {
|
||||
return GetArrayBufferData(aBuffer.GetAsArrayBufferView());
|
||||
}
|
||||
|
||||
MOZ_ASSERT(aBuffer.IsArrayBuffer());
|
||||
return GetArrayBufferData(aBuffer.GetAsArrayBuffer());
|
||||
}
|
||||
|
||||
static std::tuple<JS::ArrayBufferOrView, size_t, size_t> GetArrayBufferInfo(
|
||||
JSContext* aCx,
|
||||
const OwningMaybeSharedArrayBufferViewOrMaybeSharedArrayBuffer& aBuffer) {
|
||||
|
@ -73,12 +73,6 @@ Nullable<T> MaybeToNullable(const Maybe<T>& aOptional) {
|
||||
* Below are helpers to operate ArrayBuffer or ArrayBufferView.
|
||||
*/
|
||||
|
||||
Result<Span<uint8_t>, nsresult> GetSharedArrayBufferData(
|
||||
const MaybeSharedArrayBufferViewOrMaybeSharedArrayBuffer& aBuffer);
|
||||
|
||||
Result<Span<uint8_t>, nsresult> GetSharedArrayBufferData(
|
||||
const OwningMaybeSharedArrayBufferViewOrMaybeSharedArrayBuffer& aBuffer);
|
||||
|
||||
Result<Ok, nsresult> CloneBuffer(
|
||||
JSContext* aCx,
|
||||
OwningMaybeSharedArrayBufferViewOrMaybeSharedArrayBuffer& aDest,
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
#include "jsapi/RTCEncodedFrameBase.h"
|
||||
|
||||
#include "js/GCAPI.h"
|
||||
#include "nsIGlobalObject.h"
|
||||
#include "mozilla/dom/ScriptSettings.h"
|
||||
#include "js/ArrayBuffer.h"
|
||||
@ -45,9 +46,10 @@ unsigned long RTCEncodedFrameBase::Timestamp() const { return mTimestamp; }
|
||||
void RTCEncodedFrameBase::SetData(const ArrayBuffer& aData) {
|
||||
mData.set(aData.Obj());
|
||||
if (mFrame) {
|
||||
aData.ComputeState();
|
||||
mFrame->SetData(rtc::ArrayView<const uint8_t>(
|
||||
static_cast<const uint8_t*>(aData.Data()), aData.Length()));
|
||||
aData.ProcessData([&](const Span<uint8_t>& aData, JS::AutoCheckCannotGC&&) {
|
||||
mFrame->SetData(
|
||||
rtc::ArrayView<const uint8_t>(aData.Elements(), aData.Length()));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,7 @@
|
||||
* 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 "js/Realm.h"
|
||||
#include "mozilla/dom/EventBinding.h"
|
||||
#include "mozilla/dom/MIDIMessageEvent.h"
|
||||
#include "mozilla/dom/MIDIMessageEventBinding.h"
|
||||
@ -63,10 +64,10 @@ already_AddRefed<MIDIMessageEvent> MIDIMessageEvent::Constructor(
|
||||
// Set data for event. Timestamp will always be set to Now() (default for
|
||||
// event) using this constructor.
|
||||
if (aEventInitDict.mData.WasPassed()) {
|
||||
const auto& a = aEventInitDict.mData.Value();
|
||||
a.ComputeState();
|
||||
e->mData =
|
||||
Uint8Array::Create(aGlobal.Context(), owner, a.Length(), a.Data());
|
||||
JSAutoRealm ar(aGlobal.Context(), aGlobal.Get());
|
||||
JS::Rooted<JSObject*> data(aGlobal.Context(),
|
||||
aEventInitDict.mData.Value().Obj());
|
||||
e->mData = JS_NewUint8ArrayFromArray(aGlobal.Context(), data);
|
||||
if (NS_WARN_IF(!e->mData)) {
|
||||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
return nullptr;
|
||||
|
@ -198,36 +198,24 @@ already_AddRefed<Promise> WritableStreamToOutput::WriteCallback(
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// This is a duplicate of dom/encoding/TextDecoderStream.cpp#51-69
|
||||
// PeterV will deal with that when he lands his patch for TypedArrays
|
||||
auto dataSpan = [&data]() {
|
||||
if (data.IsArrayBuffer()) {
|
||||
const ArrayBuffer& buffer = data.GetAsArrayBuffer();
|
||||
buffer.ComputeState();
|
||||
return Span{buffer.Data(), buffer.Length()};
|
||||
}
|
||||
MOZ_ASSERT(data.IsArrayBufferView());
|
||||
const ArrayBufferView& buffer = data.GetAsArrayBufferView();
|
||||
buffer.ComputeState();
|
||||
return Span{buffer.Data(), buffer.Length()};
|
||||
}();
|
||||
|
||||
// Try to write first, and only enqueue data if we were already blocked
|
||||
// or the write didn't write it all. This avoids allocations and copies
|
||||
// in common cases.
|
||||
MOZ_ASSERT(!mPromise);
|
||||
MOZ_ASSERT(mWritten == 0);
|
||||
uint32_t written = 0;
|
||||
ProcessTypedArraysFixed(data, [&](const Span<uint8_t>& aData) {
|
||||
Span<uint8_t> dataSpan = aData;
|
||||
nsresult rv = mOutput->Write(mozilla::AsChars(dataSpan).Elements(),
|
||||
dataSpan.Length(), &written);
|
||||
if (NS_FAILED(rv) && rv != NS_BASE_STREAM_WOULD_BLOCK) {
|
||||
promise->MaybeRejectWithAbortError("error writing data");
|
||||
return promise.forget();
|
||||
return;
|
||||
}
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
if (written == dataSpan.Length()) {
|
||||
promise->MaybeResolveWithUndefined();
|
||||
return promise.forget();
|
||||
return;
|
||||
}
|
||||
dataSpan = dataSpan.From(written);
|
||||
}
|
||||
@ -235,13 +223,19 @@ already_AddRefed<Promise> WritableStreamToOutput::WriteCallback(
|
||||
auto buffer = Buffer<uint8_t>::CopyFrom(dataSpan);
|
||||
if (buffer.isNothing()) {
|
||||
promise->MaybeReject(NS_ERROR_OUT_OF_MEMORY);
|
||||
return promise.forget();
|
||||
return;
|
||||
}
|
||||
mData = std::move(buffer);
|
||||
});
|
||||
|
||||
if (promise->State() != Promise::PromiseState::Pending) {
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
mPromise = promise;
|
||||
|
||||
nsCOMPtr<nsIEventTarget> target = mozilla::GetCurrentSerialEventTarget();
|
||||
rv = mOutput->AsyncWait(this, 0, 0, target);
|
||||
nsresult rv = mOutput->AsyncWait(this, 0, 0, target);
|
||||
if (NS_FAILED(rv)) {
|
||||
ClearData();
|
||||
promise->MaybeRejectWithUnknownError("error waiting to write data");
|
||||
|
@ -2775,9 +2775,8 @@ void SyncReadFile::ReadBytesInto(const Uint8Array& aDestArray,
|
||||
return aRv.ThrowOperationError("SyncReadFile is closed");
|
||||
}
|
||||
|
||||
aDestArray.ComputeState();
|
||||
|
||||
auto rangeEnd = CheckedInt64(aOffset) + aDestArray.Length();
|
||||
aDestArray.ProcessFixedData([&](const Span<uint8_t>& aData) {
|
||||
auto rangeEnd = CheckedInt64(aOffset) + aData.Length();
|
||||
if (!rangeEnd.isValid()) {
|
||||
return aRv.ThrowOperationError("Requested range overflows i64");
|
||||
}
|
||||
@ -2787,7 +2786,7 @@ void SyncReadFile::ReadBytesInto(const Uint8Array& aDestArray,
|
||||
"Requested range overflows SyncReadFile size");
|
||||
}
|
||||
|
||||
uint32_t readLen{aDestArray.Length()};
|
||||
size_t readLen{aData.Length()};
|
||||
if (readLen == 0) {
|
||||
return;
|
||||
}
|
||||
@ -2797,18 +2796,18 @@ void SyncReadFile::ReadBytesInto(const Uint8Array& aDestArray,
|
||||
FormatErrorMessage(rv, "Could not seek to position %lld", aOffset));
|
||||
}
|
||||
|
||||
Span<char> toRead(reinterpret_cast<char*>(aDestArray.Data()), readLen);
|
||||
Span<char> toRead = AsWritableChars(aData);
|
||||
|
||||
uint32_t totalRead = 0;
|
||||
size_t totalRead = 0;
|
||||
while (totalRead != readLen) {
|
||||
// Read no more than INT32_MAX on each call to mStream->Read, otherwise it
|
||||
// returns an error.
|
||||
// Read no more than INT32_MAX on each call to mStream->Read,
|
||||
// otherwise it returns an error.
|
||||
uint32_t bytesToReadThisChunk =
|
||||
std::min<uint32_t>(readLen - totalRead, INT32_MAX);
|
||||
std::min(readLen - totalRead, size_t(INT32_MAX));
|
||||
|
||||
uint32_t bytesRead = 0;
|
||||
if (nsresult rv =
|
||||
mStream->Read(toRead.Elements(), bytesToReadThisChunk, &bytesRead);
|
||||
if (nsresult rv = mStream->Read(toRead.Elements(), bytesToReadThisChunk,
|
||||
&bytesRead);
|
||||
NS_FAILED(rv)) {
|
||||
return aRv.ThrowOperationError(FormatErrorMessage(
|
||||
rv, "Encountered an unexpected error while reading file stream"));
|
||||
@ -2820,6 +2819,7 @@ void SyncReadFile::ReadBytesInto(const Uint8Array& aDestArray,
|
||||
totalRead += bytesRead;
|
||||
toRead = toRead.From(bytesRead);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void SyncReadFile::Close() { mStream = nullptr; }
|
||||
|
@ -56,108 +56,33 @@ already_AddRefed<dom::Promise> Queue::OnSubmittedWorkDone(ErrorResult& aRv) {
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
// Get the base address and length of part of a `BufferSource`.
|
||||
//
|
||||
// Given `aBufferSource` and an offset `aDataOffset` and optional length
|
||||
// `aSizeOrRemainder` describing the range of its contents we want to see, check
|
||||
// all arguments and set `aDataContents` and `aContentsSize` to a pointer to the
|
||||
// bytes and a length. Report errors in `aRv`.
|
||||
//
|
||||
// If `ASizeOrRemainder` was not passed, return a view from the starting offset
|
||||
// to the end of `aBufferSource`.
|
||||
//
|
||||
// On success, the returned `aDataContents` is never `nullptr`. If the
|
||||
// `ArrayBuffer` is detached, return a pointer to a dummy buffer and set
|
||||
// `aContentsSize` to zero.
|
||||
//
|
||||
// The `aBufferSource` argument is a WebIDL `BufferSource`, which WebGPU methods
|
||||
// use anywhere they accept a block of raw bytes. WebIDL defines `BufferSource`
|
||||
// as:
|
||||
//
|
||||
// typedef (ArrayBufferView or ArrayBuffer) BufferSource;
|
||||
//
|
||||
// This appears in Gecko code as `dom::ArrayBufferViewOrArrayBuffer`.
|
||||
static void GetBufferSourceDataAndSize(
|
||||
const dom::ArrayBufferViewOrArrayBuffer& aBufferSource,
|
||||
uint64_t aDataOffset, const dom::Optional<uint64_t>& aSizeOrRemainder,
|
||||
uint8_t*& aDataContents, uint64_t& aContentsSize, const char* aOffsetName,
|
||||
ErrorResult& aRv) {
|
||||
uint64_t dataSize = 0;
|
||||
uint8_t* dataContents = nullptr;
|
||||
if (aBufferSource.IsArrayBufferView()) {
|
||||
const auto& view = aBufferSource.GetAsArrayBufferView();
|
||||
view.ComputeState();
|
||||
dataSize = view.Length();
|
||||
dataContents = view.Data();
|
||||
}
|
||||
if (aBufferSource.IsArrayBuffer()) {
|
||||
const auto& ab = aBufferSource.GetAsArrayBuffer();
|
||||
ab.ComputeState();
|
||||
dataSize = ab.Length();
|
||||
dataContents = ab.Data();
|
||||
}
|
||||
|
||||
if (aDataOffset > dataSize) {
|
||||
aRv.ThrowOperationError(
|
||||
nsPrintfCString("%s is greater than data length", aOffsetName));
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t contentsSize = 0;
|
||||
if (aSizeOrRemainder.WasPassed()) {
|
||||
contentsSize = aSizeOrRemainder.Value();
|
||||
} else {
|
||||
// We already know that aDataOffset <= length, so this cannot underflow.
|
||||
contentsSize = dataSize - aDataOffset;
|
||||
}
|
||||
|
||||
// This could be folded into the if above, but it's nice to make it
|
||||
// obvious that the check always occurs.
|
||||
// We already know that aDataOffset <= length, so this cannot underflow.
|
||||
if (contentsSize > dataSize - aDataOffset) {
|
||||
aRv.ThrowOperationError(
|
||||
nsPrintfCString("%s + size is greater than data length", aOffsetName));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!dataContents) {
|
||||
// Passing `nullptr` as either the source or destination to
|
||||
// `memcpy` is undefined behavior, even when the count is zero:
|
||||
//
|
||||
// https://en.cppreference.com/w/cpp/string/byte/memcpy
|
||||
//
|
||||
// We can either make callers responsible for checking the pointer
|
||||
// before calling `memcpy`, or we can have it point to a
|
||||
// permanently-live `static` dummy byte, so that the copies are
|
||||
// harmless. The latter seems less error-prone.
|
||||
static uint8_t dummy;
|
||||
dataContents = &dummy;
|
||||
MOZ_RELEASE_ASSERT(contentsSize == 0);
|
||||
}
|
||||
aDataContents = dataContents;
|
||||
aContentsSize = contentsSize;
|
||||
}
|
||||
|
||||
void Queue::WriteBuffer(const Buffer& aBuffer, uint64_t aBufferOffset,
|
||||
const dom::ArrayBufferViewOrArrayBuffer& aData,
|
||||
uint64_t aDataOffset,
|
||||
const dom::Optional<uint64_t>& aSize,
|
||||
ErrorResult& aRv) {
|
||||
uint8_t* dataContents = nullptr;
|
||||
uint64_t contentsSize = 0;
|
||||
GetBufferSourceDataAndSize(aData, aDataOffset, aSize, dataContents,
|
||||
contentsSize, "dataOffset", aRv);
|
||||
if (aRv.Failed()) {
|
||||
dom::ProcessTypedArraysFixed(aData, [&](const Span<const uint8_t>& aData) {
|
||||
uint64_t length = aData.Length();
|
||||
const auto checkedSize = aSize.WasPassed()
|
||||
? CheckedInt<size_t>(aSize.Value())
|
||||
: CheckedInt<size_t>(length) - aDataOffset;
|
||||
if (!checkedSize.isValid()) {
|
||||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
return;
|
||||
}
|
||||
|
||||
if (contentsSize % 4 != 0) {
|
||||
const auto& size = checkedSize.value();
|
||||
if (aDataOffset + size > length) {
|
||||
aRv.ThrowAbortError(nsPrintfCString("Wrong data size %" PRIuPTR, size));
|
||||
return;
|
||||
}
|
||||
|
||||
if (size % 4 != 0) {
|
||||
aRv.ThrowAbortError("Byte size must be a multiple of 4");
|
||||
return;
|
||||
}
|
||||
|
||||
auto alloc =
|
||||
mozilla::ipc::UnsafeSharedMemoryHandle::CreateAndMap(contentsSize);
|
||||
auto alloc = mozilla::ipc::UnsafeSharedMemoryHandle::CreateAndMap(size);
|
||||
if (alloc.isNothing()) {
|
||||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
return;
|
||||
@ -166,13 +91,14 @@ void Queue::WriteBuffer(const Buffer& aBuffer, uint64_t aBufferOffset,
|
||||
auto handle = std::move(alloc.ref().first);
|
||||
auto mapping = std::move(alloc.ref().second);
|
||||
|
||||
memcpy(mapping.Bytes().data(), dataContents + aDataOffset, contentsSize);
|
||||
memcpy(mapping.Bytes().data(), aData.Elements() + aDataOffset, size);
|
||||
ipc::ByteBuf bb;
|
||||
ffi::wgpu_queue_write_buffer(aBuffer.mId, aBufferOffset, ToFFI(&bb));
|
||||
if (!mBridge->SendQueueWriteAction(mId, mParent->mId, std::move(bb),
|
||||
std::move(handle))) {
|
||||
MOZ_CRASH("IPC failure");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void Queue::WriteTexture(const dom::GPUImageCopyTexture& aDestination,
|
||||
@ -187,23 +113,21 @@ void Queue::WriteTexture(const dom::GPUImageCopyTexture& aDestination,
|
||||
ffi::WGPUExtent3d extent = {};
|
||||
ConvertExtent3DToFFI(aSize, &extent);
|
||||
|
||||
uint8_t* dataContents = nullptr;
|
||||
uint64_t contentsSize = 0;
|
||||
GetBufferSourceDataAndSize(aData, aDataLayout.mOffset,
|
||||
dom::Optional<uint64_t>(), dataContents,
|
||||
contentsSize, "dataLayout.offset", aRv);
|
||||
if (aRv.Failed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!contentsSize) {
|
||||
dom::ProcessTypedArraysFixed(aData, [&](const Span<const uint8_t>& aData) {
|
||||
if (aData.IsEmpty()) {
|
||||
aRv.ThrowAbortError("Input size cannot be zero.");
|
||||
return;
|
||||
}
|
||||
MOZ_ASSERT(dataContents != nullptr);
|
||||
|
||||
auto alloc =
|
||||
mozilla::ipc::UnsafeSharedMemoryHandle::CreateAndMap(contentsSize);
|
||||
const auto checkedSize =
|
||||
CheckedInt<size_t>(aData.Length()) - aDataLayout.mOffset;
|
||||
if (!checkedSize.isValid()) {
|
||||
aRv.ThrowAbortError("Offset is higher than the size");
|
||||
return;
|
||||
}
|
||||
const auto size = checkedSize.value();
|
||||
|
||||
auto alloc = mozilla::ipc::UnsafeSharedMemoryHandle::CreateAndMap(size);
|
||||
if (alloc.isNothing()) {
|
||||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
return;
|
||||
@ -212,8 +136,8 @@ void Queue::WriteTexture(const dom::GPUImageCopyTexture& aDestination,
|
||||
auto handle = std::move(alloc.ref().first);
|
||||
auto mapping = std::move(alloc.ref().second);
|
||||
|
||||
memcpy(mapping.Bytes().data(), dataContents + aDataLayout.mOffset,
|
||||
contentsSize);
|
||||
memcpy(mapping.Bytes().data(), aData.Elements() + aDataLayout.mOffset,
|
||||
size);
|
||||
|
||||
ipc::ByteBuf bb;
|
||||
ffi::wgpu_queue_write_texture(copyView, dataLayout, extent, ToFFI(&bb));
|
||||
@ -221,6 +145,7 @@ void Queue::WriteTexture(const dom::GPUImageCopyTexture& aDestination,
|
||||
std::move(handle))) {
|
||||
MOZ_CRASH("IPC failure");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static WebGLTexelFormat ToWebGLTexelFormat(gfx::SurfaceFormat aFormat) {
|
||||
|
@ -2389,7 +2389,7 @@ void WebSocket::Send(const ArrayBuffer& aData, ErrorResult& aRv) {
|
||||
AssertIsOnTargetThread();
|
||||
|
||||
static_assert(
|
||||
sizeof(std::remove_reference<decltype(aData)>::type::element_type) == 1,
|
||||
sizeof(std::remove_reference_t<decltype(aData)>::element_type) == 1,
|
||||
"byte-sized data required");
|
||||
|
||||
nsCString msgString;
|
||||
@ -2404,7 +2404,7 @@ void WebSocket::Send(const ArrayBufferView& aData, ErrorResult& aRv) {
|
||||
AssertIsOnTargetThread();
|
||||
|
||||
static_assert(
|
||||
sizeof(std::remove_reference<decltype(aData)>::type::element_type) == 1,
|
||||
sizeof(std::remove_reference_t<decltype(aData)>::element_type) == 1,
|
||||
"byte-sized data required");
|
||||
|
||||
nsCString msgString;
|
||||
|
@ -18,33 +18,36 @@ OwnedRustBuffer::OwnedRustBuffer(const RustBuffer& aBuf) {
|
||||
|
||||
Result<OwnedRustBuffer, nsCString> OwnedRustBuffer::FromArrayBuffer(
|
||||
const ArrayBuffer& aArrayBuffer) {
|
||||
if (aArrayBuffer.Length() > INT32_MAX) {
|
||||
return aArrayBuffer.ProcessData(
|
||||
[](const Span<uint8_t>& aData,
|
||||
JS::AutoCheckCannotGC&&) -> Result<OwnedRustBuffer, nsCString> {
|
||||
if (aData.Length() > INT32_MAX) {
|
||||
return Err("Input ArrayBuffer is too large"_ns);
|
||||
}
|
||||
|
||||
RustCallStatus status{};
|
||||
RustBuffer buf = uniffi_rustbuffer_alloc(
|
||||
static_cast<int32_t>(aArrayBuffer.Length()), &status);
|
||||
buf.len = aArrayBuffer.Length();
|
||||
static_cast<int32_t>(aData.Length()), &status);
|
||||
buf.len = aData.Length();
|
||||
if (status.code != 0) {
|
||||
if (status.error_buf.data) {
|
||||
auto message = nsCString("uniffi_rustbuffer_alloc: ");
|
||||
message.Append(
|
||||
nsDependentCSubstring(reinterpret_cast<char*>(status.error_buf.data),
|
||||
message.Append(nsDependentCSubstring(
|
||||
reinterpret_cast<char*>(status.error_buf.data),
|
||||
status.error_buf.len));
|
||||
RustCallStatus status2{};
|
||||
uniffi_rustbuffer_free(status.error_buf, &status2);
|
||||
MOZ_RELEASE_ASSERT(status2.code == 0,
|
||||
"Freeing a rustbuffer should never fail");
|
||||
return Err(message);
|
||||
}
|
||||
|
||||
} else {
|
||||
return Err("Unknown error allocating rust buffer"_ns);
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(buf.data, aArrayBuffer.Data(), buf.len);
|
||||
memcpy(buf.data, aData.Elements(), buf.len);
|
||||
return OwnedRustBuffer(buf);
|
||||
});
|
||||
}
|
||||
|
||||
OwnedRustBuffer::OwnedRustBuffer(OwnedRustBuffer&& aOther) : mBuf(aOther.mBuf) {
|
||||
|
@ -139,9 +139,7 @@ class ScaffoldingConverter<RustBuffer> {
|
||||
return Err("Bad argument type"_ns);
|
||||
}
|
||||
|
||||
const dom::ArrayBuffer& arrayBuf = aValue.GetAsArrayBuffer();
|
||||
arrayBuf.ComputeState();
|
||||
return OwnedRustBuffer::FromArrayBuffer(arrayBuf);
|
||||
return OwnedRustBuffer::FromArrayBuffer(aValue.GetAsArrayBuffer());
|
||||
}
|
||||
|
||||
static RustBuffer IntoRust(OwnedRustBuffer&& aValue) {
|
||||
|
@ -71,18 +71,19 @@ void UniFFIPointer::Write(const ArrayBuffer& aArrayBuff, uint32_t aPosition,
|
||||
}
|
||||
MOZ_LOG(sUniFFIPointerLogger, LogLevel::Info,
|
||||
("[UniFFI] Writing Pointer to buffer"));
|
||||
aArrayBuff.ComputeState();
|
||||
CheckedUint32 position = aPosition;
|
||||
CheckedUint32 end = position + 8;
|
||||
if (!end.isValid() || end.value() > aArrayBuff.Length()) {
|
||||
|
||||
aArrayBuff.ProcessData([&](const Span<uint8_t>& aData,
|
||||
JS::AutoCheckCannotGC&&) {
|
||||
CheckedUint32 end = aPosition + 8;
|
||||
if (!end.isValid() || end.value() > aData.Length()) {
|
||||
aError.ThrowRangeError("position is out of range");
|
||||
return;
|
||||
}
|
||||
// in Rust and Read(), a u64 is read as BigEndian and then converted to a
|
||||
// pointer we do the reverse here
|
||||
uint8_t* data_ptr = aArrayBuff.Data() +
|
||||
aPosition; // Pointer arithmetic, move by position bytes
|
||||
mozilla::BigEndian::writeUint64(data_ptr, (uint64_t)GetPtr());
|
||||
// in Rust and Read(), a u64 is read as BigEndian and then converted to
|
||||
// a pointer we do the reverse here
|
||||
const auto& data_ptr = aData.Subspan(aPosition, 8);
|
||||
mozilla::BigEndian::writeUint64(data_ptr.Elements(), (uint64_t)GetPtr());
|
||||
});
|
||||
}
|
||||
|
||||
UniFFIPointer::UniFFIPointer(void* aPtr, const UniFFIPointerType* aType) {
|
||||
|
Loading…
Reference in New Issue
Block a user