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 ArrayBufferViewOrArrayBuffer& aSource,
|
||||||
const Base64URLEncodeOptions& aOptions,
|
const Base64URLEncodeOptions& aOptions,
|
||||||
nsACString& aResult, ErrorResult& aRv) {
|
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
|
auto paddingPolicy = aOptions.mPad ? Base64URLEncodePaddingPolicy::Include
|
||||||
: Base64URLEncodePaddingPolicy::Omit;
|
: Base64URLEncodePaddingPolicy::Omit;
|
||||||
nsresult rv = mozilla::Base64URLEncode(length, data, paddingPolicy, aResult);
|
ProcessTypedArrays(
|
||||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
aSource, [&](const Span<uint8_t>& aData, JS::AutoCheckCannotGC&&) {
|
||||||
aResult.Truncate();
|
nsresult rv = mozilla::Base64URLEncode(aData.Length(), aData.Elements(),
|
||||||
aRv.Throw(rv);
|
paddingPolicy, aResult);
|
||||||
}
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
aResult.Truncate();
|
||||||
|
aRv.Throw(rv);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/* static */
|
/* static */
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#include "js/TypeDecls.h"
|
#include "js/TypeDecls.h"
|
||||||
#include "mozilla/Assertions.h"
|
#include "mozilla/Assertions.h"
|
||||||
|
#include "mozilla/Attributes.h"
|
||||||
#include "mozilla/dom/CompressionStreamBinding.h"
|
#include "mozilla/dom/CompressionStreamBinding.h"
|
||||||
#include "mozilla/dom/ReadableStream.h"
|
#include "mozilla/dom/ReadableStream.h"
|
||||||
#include "mozilla/dom/WritableStream.h"
|
#include "mozilla/dom/WritableStream.h"
|
||||||
@ -57,16 +58,21 @@ class CompressionStreamAlgorithms : public TransformerAlgorithmsWrapper {
|
|||||||
// https://wicg.github.io/compression/#compress-and-enqueue-a-chunk
|
// https://wicg.github.io/compression/#compress-and-enqueue-a-chunk
|
||||||
|
|
||||||
// Step 1: If chunk is not a BufferSource type, then throw a TypeError.
|
// Step 1: If chunk is not a BufferSource type, then throw a TypeError.
|
||||||
// (ExtractSpanFromBufferSource does it)
|
RootedUnion<OwningArrayBufferViewOrArrayBuffer> bufferSource(cx);
|
||||||
Span<const uint8_t> input = ExtractSpanFromBufferSource(cx, aChunk, aRv);
|
if (!bufferSource.Init(cx, aChunk)) {
|
||||||
if (aRv.Failed()) {
|
aRv.MightThrowJSException();
|
||||||
|
aRv.StealExceptionFromJSContext(cx);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 2: Let buffer be the result of compressing chunk with cs's format
|
// Step 2: Let buffer be the result of compressing chunk with cs's format
|
||||||
// and context.
|
// and context.
|
||||||
// Step 3 - 5: (Done in CompressAndEnqueue)
|
// 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
|
// Step 4 of
|
||||||
|
@ -40,11 +40,9 @@ JSObject* Crypto::WrapObject(JSContext* aCx,
|
|||||||
void Crypto::GetRandomValues(JSContext* aCx, const ArrayBufferView& aArray,
|
void Crypto::GetRandomValues(JSContext* aCx, const ArrayBufferView& aArray,
|
||||||
JS::MutableHandle<JSObject*> aRetval,
|
JS::MutableHandle<JSObject*> aRetval,
|
||||||
ErrorResult& aRv) {
|
ErrorResult& aRv) {
|
||||||
JS::Rooted<JSObject*> view(aCx, aArray.Obj());
|
|
||||||
|
|
||||||
// Throw if the wrong type of ArrayBufferView is passed in
|
// Throw if the wrong type of ArrayBufferView is passed in
|
||||||
// (Part of the Web Crypto API spec)
|
// (Part of the Web Crypto API spec)
|
||||||
switch (JS_GetArrayBufferViewType(view)) {
|
switch (aArray.Type()) {
|
||||||
case js::Scalar::Int8:
|
case js::Scalar::Int8:
|
||||||
case js::Scalar::Uint8:
|
case js::Scalar::Uint8:
|
||||||
case js::Scalar::Uint8Clamped:
|
case js::Scalar::Uint8Clamped:
|
||||||
@ -60,17 +58,6 @@ void Crypto::GetRandomValues(JSContext* aCx, const ArrayBufferView& aArray,
|
|||||||
return;
|
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 =
|
nsCOMPtr<nsIRandomGenerator> randomGenerator =
|
||||||
do_GetService("@mozilla.org/security/random-generator;1");
|
do_GetService("@mozilla.org/security/random-generator;1");
|
||||||
if (!randomGenerator) {
|
if (!randomGenerator) {
|
||||||
@ -78,14 +65,27 @@ void Crypto::GetRandomValues(JSContext* aCx, const ArrayBufferView& aArray,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult rv =
|
aArray.ProcessFixedData([&](const Span<uint8_t>& aData) {
|
||||||
randomGenerator->GenerateRandomBytesInto(aArray.Data(), dataLen);
|
if (aData.Length() == 0) {
|
||||||
if (NS_FAILED(rv)) {
|
NS_WARNING("ArrayBufferView length is 0, cannot continue");
|
||||||
aRv.Throw(NS_ERROR_DOM_OPERATION_ERR);
|
aRetval.set(aArray.Obj());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
aRetval.set(view);
|
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(aArray.Obj());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void Crypto::RandomUUID(nsACString& aRetVal) {
|
void Crypto::RandomUUID(nsACString& aRetVal) {
|
||||||
|
@ -199,29 +199,33 @@ already_AddRefed<DOMMatrixReadOnly> DOMMatrixReadOnly::FromMatrix(
|
|||||||
already_AddRefed<DOMMatrixReadOnly> DOMMatrixReadOnly::FromFloat32Array(
|
already_AddRefed<DOMMatrixReadOnly> DOMMatrixReadOnly::FromFloat32Array(
|
||||||
const GlobalObject& aGlobal, const Float32Array& aArray32,
|
const GlobalObject& aGlobal, const Float32Array& aArray32,
|
||||||
ErrorResult& aRv) {
|
ErrorResult& aRv) {
|
||||||
aArray32.ComputeState();
|
nsCOMPtr<nsISupports> global = aGlobal.GetAsSupports();
|
||||||
|
return aArray32.ProcessData(
|
||||||
const int length = aArray32.Length();
|
[&](const Span<float>& aData, JS::AutoCheckCannotGC&& nogc) {
|
||||||
const bool is2D = length == 6;
|
const int length = aData.Length();
|
||||||
RefPtr<DOMMatrixReadOnly> obj =
|
const bool is2D = length == 6;
|
||||||
new DOMMatrixReadOnly(aGlobal.GetAsSupports(), is2D);
|
RefPtr<DOMMatrixReadOnly> obj =
|
||||||
SetDataInMatrix(obj, aArray32.Data(), length, aRv);
|
new DOMMatrixReadOnly(global.forget(), is2D);
|
||||||
|
SetDataInMatrix(obj, aData.Elements(), length, aRv);
|
||||||
return obj.forget();
|
nogc.reset(); // Done with aData
|
||||||
|
return obj.forget();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
already_AddRefed<DOMMatrixReadOnly> DOMMatrixReadOnly::FromFloat64Array(
|
already_AddRefed<DOMMatrixReadOnly> DOMMatrixReadOnly::FromFloat64Array(
|
||||||
const GlobalObject& aGlobal, const Float64Array& aArray64,
|
const GlobalObject& aGlobal, const Float64Array& aArray64,
|
||||||
ErrorResult& aRv) {
|
ErrorResult& aRv) {
|
||||||
aArray64.ComputeState();
|
nsCOMPtr<nsISupports> global = aGlobal.GetAsSupports();
|
||||||
|
return aArray64.ProcessData(
|
||||||
const int length = aArray64.Length();
|
[&](const Span<double>& aData, JS::AutoCheckCannotGC&& nogc) {
|
||||||
const bool is2D = length == 6;
|
const int length = aData.Length();
|
||||||
RefPtr<DOMMatrixReadOnly> obj =
|
const bool is2D = length == 6;
|
||||||
new DOMMatrixReadOnly(aGlobal.GetAsSupports(), is2D);
|
RefPtr<DOMMatrixReadOnly> obj =
|
||||||
SetDataInMatrix(obj, aArray64.Data(), length, aRv);
|
new DOMMatrixReadOnly(global.forget(), is2D);
|
||||||
|
SetDataInMatrix(obj, aData.Elements(), length, aRv);
|
||||||
return obj.forget();
|
nogc.reset(); // Done with aData
|
||||||
|
return obj.forget();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
already_AddRefed<DOMMatrixReadOnly> DOMMatrixReadOnly::Constructor(
|
already_AddRefed<DOMMatrixReadOnly> DOMMatrixReadOnly::Constructor(
|
||||||
@ -642,27 +646,31 @@ already_AddRefed<DOMMatrix> DOMMatrix::FromMatrix(
|
|||||||
already_AddRefed<DOMMatrix> DOMMatrix::FromFloat32Array(
|
already_AddRefed<DOMMatrix> DOMMatrix::FromFloat32Array(
|
||||||
const GlobalObject& aGlobal, const Float32Array& aArray32,
|
const GlobalObject& aGlobal, const Float32Array& aArray32,
|
||||||
ErrorResult& aRv) {
|
ErrorResult& aRv) {
|
||||||
aArray32.ComputeState();
|
nsCOMPtr<nsISupports> global = aGlobal.GetAsSupports();
|
||||||
|
return aArray32.ProcessData(
|
||||||
const int length = aArray32.Length();
|
[&](const Span<float>& aData, JS::AutoCheckCannotGC&& nogc) {
|
||||||
const bool is2D = length == 6;
|
const int length = aData.Length();
|
||||||
RefPtr<DOMMatrix> obj = new DOMMatrix(aGlobal.GetAsSupports(), is2D);
|
const bool is2D = length == 6;
|
||||||
SetDataInMatrix(obj, aArray32.Data(), length, aRv);
|
RefPtr<DOMMatrix> obj = new DOMMatrix(global.forget(), is2D);
|
||||||
|
SetDataInMatrix(obj, aData.Elements(), length, aRv);
|
||||||
return obj.forget();
|
nogc.reset(); // Done with aData
|
||||||
|
return obj.forget();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
already_AddRefed<DOMMatrix> DOMMatrix::FromFloat64Array(
|
already_AddRefed<DOMMatrix> DOMMatrix::FromFloat64Array(
|
||||||
const GlobalObject& aGlobal, const Float64Array& aArray64,
|
const GlobalObject& aGlobal, const Float64Array& aArray64,
|
||||||
ErrorResult& aRv) {
|
ErrorResult& aRv) {
|
||||||
aArray64.ComputeState();
|
nsCOMPtr<nsISupports> global = aGlobal.GetAsSupports();
|
||||||
|
return aArray64.ProcessData(
|
||||||
const int length = aArray64.Length();
|
[&](const Span<double>& aData, JS::AutoCheckCannotGC&& nogc) {
|
||||||
const bool is2D = length == 6;
|
const int length = aData.Length();
|
||||||
RefPtr<DOMMatrix> obj = new DOMMatrix(aGlobal.GetAsSupports(), is2D);
|
const bool is2D = length == 6;
|
||||||
SetDataInMatrix(obj, aArray64.Data(), length, aRv);
|
RefPtr<DOMMatrix> obj = new DOMMatrix(global.forget(), is2D);
|
||||||
|
SetDataInMatrix(obj, aData.Elements(), length, aRv);
|
||||||
return obj.forget();
|
nogc.reset(); // Done with aData
|
||||||
|
return obj.forget();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
already_AddRefed<DOMMatrix> DOMMatrix::Constructor(
|
already_AddRefed<DOMMatrix> DOMMatrix::Constructor(
|
||||||
|
@ -254,7 +254,10 @@ class DOMMatrixReadOnly : public nsWrapperCache {
|
|||||||
DOMMatrixReadOnly* SetMatrixValue(const nsACString&, ErrorResult&);
|
DOMMatrixReadOnly* SetMatrixValue(const nsACString&, ErrorResult&);
|
||||||
void Ensure3DMatrix();
|
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) {
|
if (is2D) {
|
||||||
mMatrix2D = MakeUnique<gfx::MatrixDouble>();
|
mMatrix2D = MakeUnique<gfx::MatrixDouble>();
|
||||||
} else {
|
} else {
|
||||||
@ -335,6 +338,8 @@ class DOMMatrix : public DOMMatrixReadOnly {
|
|||||||
private:
|
private:
|
||||||
DOMMatrix(nsISupports* aParent, bool is2D)
|
DOMMatrix(nsISupports* aParent, bool is2D)
|
||||||
: DOMMatrixReadOnly(aParent, is2D) {}
|
: DOMMatrixReadOnly(aParent, is2D) {}
|
||||||
|
DOMMatrix(already_AddRefed<nsISupports>&& aParent, bool is2D)
|
||||||
|
: DOMMatrixReadOnly(std::move(aParent), is2D) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace dom
|
} // namespace dom
|
||||||
|
@ -120,8 +120,9 @@ already_AddRefed<Document> DOMParser::ParseFromSafeString(const nsAString& aStr,
|
|||||||
already_AddRefed<Document> DOMParser::ParseFromBuffer(const Uint8Array& aBuf,
|
already_AddRefed<Document> DOMParser::ParseFromBuffer(const Uint8Array& aBuf,
|
||||||
SupportedType aType,
|
SupportedType aType,
|
||||||
ErrorResult& aRv) {
|
ErrorResult& aRv) {
|
||||||
aBuf.ComputeState();
|
return aBuf.ProcessFixedData([&](const Span<uint8_t>& aData) {
|
||||||
return ParseFromBuffer(Span(aBuf.Data(), aBuf.Length()), aType, aRv);
|
return ParseFromBuffer(aData, aType, aRv);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
already_AddRefed<Document> DOMParser::ParseFromBuffer(Span<const uint8_t> aBuf,
|
already_AddRefed<Document> DOMParser::ParseFromBuffer(Span<const uint8_t> aBuf,
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
#include "mozilla/dom/TextDecoderStream.h"
|
#include "mozilla/dom/TextDecoderStream.h"
|
||||||
#include "mozilla/dom/TransformStream.h"
|
#include "mozilla/dom/TransformStream.h"
|
||||||
#include "mozilla/dom/TransformerCallbackHelpers.h"
|
#include "mozilla/dom/TransformerCallbackHelpers.h"
|
||||||
|
#include "mozilla/dom/UnionTypes.h"
|
||||||
|
|
||||||
#include "ZLibHelper.h"
|
#include "ZLibHelper.h"
|
||||||
|
|
||||||
@ -54,16 +55,21 @@ class DecompressionStreamAlgorithms : public TransformerAlgorithmsWrapper {
|
|||||||
// https://wicg.github.io/compression/#compress-and-enqueue-a-chunk
|
// https://wicg.github.io/compression/#compress-and-enqueue-a-chunk
|
||||||
|
|
||||||
// Step 1: If chunk is not a BufferSource type, then throw a TypeError.
|
// Step 1: If chunk is not a BufferSource type, then throw a TypeError.
|
||||||
// (ExtractSpanFromBufferSource does it)
|
RootedUnion<OwningArrayBufferViewOrArrayBuffer> bufferSource(cx);
|
||||||
Span<const uint8_t> input = ExtractSpanFromBufferSource(cx, aChunk, aRv);
|
if (!bufferSource.Init(cx, aChunk)) {
|
||||||
if (aRv.Failed()) {
|
aRv.MightThrowJSException();
|
||||||
|
aRv.StealExceptionFromJSContext(cx);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 2: Let buffer be the result of decompressing chunk with ds's format
|
// 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.
|
// and context. If this results in an error, then throw a TypeError.
|
||||||
// Step 3 - 5: (Done in CompressAndEnqueue)
|
// 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
|
// Step 4 of
|
||||||
|
@ -415,18 +415,6 @@ struct TypedArray_base : public SpiderMonkeyInterfaceObjectStorage,
|
|||||||
return mData;
|
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 {
|
inline uint32_t Length() const {
|
||||||
MOZ_ASSERT(mComputed);
|
MOZ_ASSERT(mComputed);
|
||||||
return mLength;
|
return mLength;
|
||||||
@ -714,6 +702,7 @@ struct TypedArray_base : public SpiderMonkeyInterfaceObjectStorage,
|
|||||||
return CallProcessor(GetCurrentData(), std::forward<Processor>(aProcessor));
|
return CallProcessor(GetCurrentData(), std::forward<Processor>(aProcessor));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
inline void Reset() {
|
inline void Reset() {
|
||||||
// This method mostly exists to inform the GC rooting hazard analysis that
|
// 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
|
// the variable can be considered dead, at least until you do anything else
|
||||||
|
@ -5907,8 +5907,7 @@ nsresult CanvasRenderingContext2D::GetImageDataArray(
|
|||||||
nsIPrincipal& aSubjectPrincipal, JSObject** aRetval) {
|
nsIPrincipal& aSubjectPrincipal, JSObject** aRetval) {
|
||||||
MOZ_ASSERT(aWidth && aHeight);
|
MOZ_ASSERT(aWidth && aHeight);
|
||||||
|
|
||||||
// Restrict the typed array length to INT32_MAX because that's all we support
|
// Restrict the typed array length to INT32_MAX because that's all we support.
|
||||||
// in dom::TypedArray::ComputeState.
|
|
||||||
CheckedInt<uint32_t> len = CheckedInt<uint32_t>(aWidth) * aHeight * 4;
|
CheckedInt<uint32_t> len = CheckedInt<uint32_t>(aWidth) * aHeight * 4;
|
||||||
if (!len.isValid() || len.value() > INT32_MAX) {
|
if (!len.isValid() || len.value() > INT32_MAX) {
|
||||||
return NS_ERROR_DOM_INDEX_SIZE_ERR;
|
return NS_ERROR_DOM_INDEX_SIZE_ERR;
|
||||||
@ -6137,18 +6136,13 @@ void CanvasRenderingContext2D::PutImageData_explicit(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
arr.ComputeState();
|
RefPtr<DataSourceSurface> sourceSurface;
|
||||||
|
uint8_t* lockedBits = nullptr;
|
||||||
|
|
||||||
uint32_t dataLen = arr.Length();
|
// The canvas spec says that the current path, transformation matrix,
|
||||||
|
// shadow attributes, global alpha, the clipping region, and global
|
||||||
uint32_t len = width * height * 4;
|
// composition operator must not affect the getImageData() and
|
||||||
if (dataLen != len) {
|
// putImageData() methods.
|
||||||
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.
|
|
||||||
const gfx::Rect putRect(dirtyRect);
|
const gfx::Rect putRect(dirtyRect);
|
||||||
EnsureTarget(&putRect);
|
EnsureTarget(&putRect);
|
||||||
|
|
||||||
@ -6157,8 +6151,6 @@ void CanvasRenderingContext2D::PutImageData_explicit(
|
|||||||
}
|
}
|
||||||
|
|
||||||
DataSourceSurface::MappedSurface map;
|
DataSourceSurface::MappedSurface map;
|
||||||
RefPtr<DataSourceSurface> sourceSurface;
|
|
||||||
uint8_t* lockedBits = nullptr;
|
|
||||||
uint8_t* dstData;
|
uint8_t* dstData;
|
||||||
IntSize dstSize;
|
IntSize dstSize;
|
||||||
int32_t dstStride;
|
int32_t dstStride;
|
||||||
@ -6169,10 +6161,11 @@ void CanvasRenderingContext2D::PutImageData_explicit(
|
|||||||
sourceSurface = Factory::CreateDataSourceSurface(
|
sourceSurface = Factory::CreateDataSourceSurface(
|
||||||
dirtyRect.Size(), SurfaceFormat::B8G8R8A8, false);
|
dirtyRect.Size(), SurfaceFormat::B8G8R8A8, false);
|
||||||
|
|
||||||
// In certain scenarios, requesting larger than 8k image fails. Bug 803568
|
// In certain scenarios, requesting larger than 8k image fails. Bug
|
||||||
// covers the details of how to run into it, but the full detailed
|
// 803568 covers the details of how to run into it, but the full
|
||||||
// investigation hasn't been done to determine the underlying cause. We
|
// detailed investigation hasn't been done to determine the
|
||||||
// will just handle the failure to allocate the surface to avoid a crash.
|
// underlying cause. We will just handle the failure to allocate
|
||||||
|
// the surface to avoid a crash.
|
||||||
if (!sourceSurface) {
|
if (!sourceSurface) {
|
||||||
return aRv.Throw(NS_ERROR_FAILURE);
|
return aRv.Throw(NS_ERROR_FAILURE);
|
||||||
}
|
}
|
||||||
@ -6188,12 +6181,27 @@ void CanvasRenderingContext2D::PutImageData_explicit(
|
|||||||
dstFormat = sourceSurface->GetFormat();
|
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(
|
uint8_t* srcData =
|
||||||
srcData, width * 4, SurfaceFormat::R8G8B8A8, dstData, dstStride,
|
aData.Elements() + srcRect.y * (width * 4) + srcRect.x * 4;
|
||||||
mOpaque ? SurfaceFormat::X8R8G8B8_UINT32 : SurfaceFormat::A8R8G8B8_UINT32,
|
|
||||||
dirtyRect.Size());
|
PremultiplyData(srcData, width * 4, SurfaceFormat::R8G8B8A8, dstData,
|
||||||
|
dstStride,
|
||||||
|
mOpaque ? SurfaceFormat::X8R8G8B8_UINT32
|
||||||
|
: SurfaceFormat::A8R8G8B8_UINT32,
|
||||||
|
dirtyRect.Size());
|
||||||
|
});
|
||||||
|
|
||||||
|
if (aRv.Failed()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (lockedBits) {
|
if (lockedBits) {
|
||||||
mTarget->ReleaseBits(lockedBits);
|
mTarget->ReleaseBits(lockedBits);
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
#include "js/ScalarType.h" // js::Scalar::Type
|
#include "js/ScalarType.h" // js::Scalar::Type
|
||||||
#include "mozilla/dom/Document.h"
|
#include "mozilla/dom/Document.h"
|
||||||
#include "mozilla/dom/ToJSValue.h"
|
#include "mozilla/dom/ToJSValue.h"
|
||||||
|
#include "mozilla/dom/TypedArray.h"
|
||||||
#include "mozilla/dom/WebGLContextEvent.h"
|
#include "mozilla/dom/WebGLContextEvent.h"
|
||||||
#include "mozilla/dom/WorkerCommon.h"
|
#include "mozilla/dom/WorkerCommon.h"
|
||||||
#include "mozilla/EnumeratedRange.h"
|
#include "mozilla/EnumeratedRange.h"
|
||||||
@ -434,6 +435,44 @@ void ClientWebGLContext::Run(Args&&... args) const {
|
|||||||
webgl::Serialize(destBytes, id, args...);
|
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.
|
// Client-side helper methods. Dispatch to a Host method.
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
@ -2873,13 +2912,14 @@ void ClientWebGLContext::Clear(GLbitfield mask) {
|
|||||||
void ClientWebGLContext::ClearBufferTv(const GLenum buffer,
|
void ClientWebGLContext::ClearBufferTv(const GLenum buffer,
|
||||||
const GLint drawBuffer,
|
const GLint drawBuffer,
|
||||||
const webgl::AttribBaseType type,
|
const webgl::AttribBaseType type,
|
||||||
const Range<const uint8_t>& view,
|
JS::AutoCheckCannotGC&& nogc,
|
||||||
|
const Span<const uint8_t>& view,
|
||||||
const GLuint srcElemOffset) {
|
const GLuint srcElemOffset) {
|
||||||
const FuncScope funcScope(*this, "clearBufferu?[fi]v");
|
|
||||||
if (IsContextLost()) return;
|
if (IsContextLost()) return;
|
||||||
|
|
||||||
const auto byteOffset = CheckedInt<size_t>(srcElemOffset) * sizeof(float);
|
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`.");
|
EnqueueError(LOCAL_GL_INVALID_VALUE, "`srcOffset` too large for `values`.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -2900,17 +2940,20 @@ void ClientWebGLContext::ClearBufferTv(const GLenum buffer,
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
nogc.reset();
|
||||||
EnqueueError_ArgEnum("buffer", buffer);
|
EnqueueError_ArgEnum("buffer", buffer);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto requiredBytes = byteOffset + dataSize;
|
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.");
|
EnqueueError(LOCAL_GL_INVALID_VALUE, "`values` too small.");
|
||||||
return;
|
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);
|
Run<RPROC(ClearBufferTv)>(buffer, drawBuffer, data);
|
||||||
|
|
||||||
AfterDrawCall();
|
AfterDrawCall();
|
||||||
@ -3356,6 +3399,14 @@ void ClientWebGLContext::BindBufferRangeImpl(const GLenum target,
|
|||||||
size);
|
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,
|
void ClientWebGLContext::GetBufferSubData(GLenum target, GLintptr srcByteOffset,
|
||||||
const dom::ArrayBufferView& dstData,
|
const dom::ArrayBufferView& dstData,
|
||||||
GLuint dstElemOffset,
|
GLuint dstElemOffset,
|
||||||
@ -3366,42 +3417,44 @@ void ClientWebGLContext::GetBufferSubData(GLenum target, GLintptr srcByteOffset,
|
|||||||
mNotLost; // Hold a strong-ref to prevent LoseContext=>UAF.
|
mNotLost; // Hold a strong-ref to prevent LoseContext=>UAF.
|
||||||
if (!ValidateNonNegative("srcByteOffset", srcByteOffset)) return;
|
if (!ValidateNonNegative("srcByteOffset", srcByteOffset)) return;
|
||||||
|
|
||||||
uint8_t* bytes;
|
size_t elemSize = SizeOfViewElem(dstData);
|
||||||
size_t byteLen;
|
dstData.ProcessFixedData([&](const Span<uint8_t>& aData) {
|
||||||
if (!ValidateArrayBufferView(dstData, dstElemOffset, dstElemCountOverride,
|
const auto& destView =
|
||||||
LOCAL_GL_INVALID_VALUE, &bytes, &byteLen)) {
|
ValidateArrayBufferView(aData, elemSize, dstElemOffset,
|
||||||
return;
|
dstElemCountOverride, LOCAL_GL_INVALID_VALUE);
|
||||||
}
|
if (!destView) {
|
||||||
const auto destView = Range<uint8_t>{bytes, byteLen};
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const auto& inProcessContext = notLost->inProcess;
|
const auto& inProcessContext = notLost->inProcess;
|
||||||
if (inProcessContext) {
|
if (inProcessContext) {
|
||||||
inProcessContext->GetBufferSubData(target, srcByteOffset, destView);
|
inProcessContext->GetBufferSubData(target, srcByteOffset, *destView);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& child = notLost->outOfProcess;
|
const auto& child = notLost->outOfProcess;
|
||||||
child->FlushPendingCmds();
|
child->FlushPendingCmds();
|
||||||
mozilla::ipc::Shmem rawShmem;
|
mozilla::ipc::Shmem rawShmem;
|
||||||
if (!child->SendGetBufferSubData(target, srcByteOffset, destView.length(),
|
if (!child->SendGetBufferSubData(target, srcByteOffset, destView->length(),
|
||||||
&rawShmem)) {
|
&rawShmem)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const webgl::RaiiShmem shmem{child, rawShmem};
|
const webgl::RaiiShmem shmem{child, rawShmem};
|
||||||
if (!shmem) {
|
if (!shmem) {
|
||||||
EnqueueError(LOCAL_GL_OUT_OF_MEMORY, "Failed to map in sub data buffer.");
|
EnqueueError(LOCAL_GL_OUT_OF_MEMORY, "Failed to map in sub data buffer.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto shmemView = shmem.ByteRange();
|
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 ok = bool(*(shmemView.begin().get()));
|
||||||
const auto srcView =
|
const auto srcView =
|
||||||
Range<const uint8_t>{shmemView.begin() + 1, shmemView.end()};
|
Range<const uint8_t>{shmemView.begin() + 1, shmemView.end()};
|
||||||
if (ok) {
|
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;
|
if (!ValidateNonNull("src", maybeSrc)) return;
|
||||||
const auto& src = maybeSrc.Value();
|
const auto& src = maybeSrc.Value();
|
||||||
|
|
||||||
src.ComputeState();
|
src.ProcessFixedData([&](const Span<uint8_t>& aData) {
|
||||||
const auto range = Range<const uint8_t>{src.Data(), src.Length()};
|
Run<RPROC(BufferData)>(target, RawBuffer<>(aData), usage);
|
||||||
Run<RPROC(BufferData)>(target, RawBuffer<>(range), usage);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientWebGLContext::BufferData(GLenum target,
|
void ClientWebGLContext::BufferData(GLenum target,
|
||||||
@ -3438,14 +3491,16 @@ void ClientWebGLContext::BufferData(GLenum target,
|
|||||||
GLenum usage, GLuint srcElemOffset,
|
GLenum usage, GLuint srcElemOffset,
|
||||||
GLuint srcElemCountOverride) {
|
GLuint srcElemCountOverride) {
|
||||||
const FuncScope funcScope(*this, "bufferData");
|
const FuncScope funcScope(*this, "bufferData");
|
||||||
uint8_t* bytes;
|
size_t elemSize = SizeOfViewElem(src);
|
||||||
size_t byteLen;
|
src.ProcessFixedData([&](const Span<uint8_t>& aData) {
|
||||||
if (!ValidateArrayBufferView(src, srcElemOffset, srcElemCountOverride,
|
const auto& range =
|
||||||
LOCAL_GL_INVALID_VALUE, &bytes, &byteLen)) {
|
ValidateArrayBufferView(aData, elemSize, srcElemOffset,
|
||||||
return;
|
srcElemCountOverride, LOCAL_GL_INVALID_VALUE);
|
||||||
}
|
if (!range) {
|
||||||
const auto range = Range<const uint8_t>{bytes, byteLen};
|
return;
|
||||||
Run<RPROC(BufferData)>(target, RawBuffer<>(range), usage);
|
}
|
||||||
|
Run<RPROC(BufferData)>(target, RawBuffer<>(*range), usage);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientWebGLContext::RawBufferData(GLenum target, const uint8_t* srcBytes,
|
void ClientWebGLContext::RawBufferData(GLenum target, const uint8_t* srcBytes,
|
||||||
@ -3473,10 +3528,10 @@ void ClientWebGLContext::BufferSubData(GLenum target,
|
|||||||
WebGLsizeiptr dstByteOffset,
|
WebGLsizeiptr dstByteOffset,
|
||||||
const dom::ArrayBuffer& src) {
|
const dom::ArrayBuffer& src) {
|
||||||
const FuncScope funcScope(*this, "bufferSubData");
|
const FuncScope funcScope(*this, "bufferSubData");
|
||||||
src.ComputeState();
|
src.ProcessFixedData([&](const Span<uint8_t>& aData) {
|
||||||
const auto range = Range<const uint8_t>{src.Data(), src.Length()};
|
Run<RPROC(BufferSubData)>(target, dstByteOffset, RawBuffer<>(aData),
|
||||||
Run<RPROC(BufferSubData)>(target, dstByteOffset, RawBuffer<>(range),
|
/* unsynchronized */ false);
|
||||||
/* unsynchronized */ false);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientWebGLContext::BufferSubData(GLenum target,
|
void ClientWebGLContext::BufferSubData(GLenum target,
|
||||||
@ -3485,15 +3540,17 @@ void ClientWebGLContext::BufferSubData(GLenum target,
|
|||||||
GLuint srcElemOffset,
|
GLuint srcElemOffset,
|
||||||
GLuint srcElemCountOverride) {
|
GLuint srcElemCountOverride) {
|
||||||
const FuncScope funcScope(*this, "bufferSubData");
|
const FuncScope funcScope(*this, "bufferSubData");
|
||||||
uint8_t* bytes;
|
size_t elemSize = SizeOfViewElem(src);
|
||||||
size_t byteLen;
|
src.ProcessFixedData([&](const Span<uint8_t>& aData) {
|
||||||
if (!ValidateArrayBufferView(src, srcElemOffset, srcElemCountOverride,
|
const auto& range =
|
||||||
LOCAL_GL_INVALID_VALUE, &bytes, &byteLen)) {
|
ValidateArrayBufferView(aData, elemSize, srcElemOffset,
|
||||||
return;
|
srcElemCountOverride, LOCAL_GL_INVALID_VALUE);
|
||||||
}
|
if (!range) {
|
||||||
const auto range = Range<const uint8_t>{bytes, byteLen};
|
return;
|
||||||
Run<RPROC(BufferSubData)>(target, dstByteOffset, RawBuffer<>(range),
|
}
|
||||||
/* unsynchronized */ false);
|
Run<RPROC(BufferSubData)>(target, dstByteOffset, RawBuffer<>(*range),
|
||||||
|
/* unsynchronized */ false);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientWebGLContext::CopyBufferSubData(GLenum readTarget,
|
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};
|
return Range<T>{newBegin, newBegin + length};
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline size_t SizeOfViewElem(const dom::ArrayBufferView& view) {
|
Maybe<Range<const uint8_t>> GetRangeFromData(const Span<uint8_t>& data,
|
||||||
const auto& elemType = view.Type();
|
size_t bytesPerElem,
|
||||||
if (elemType == js::Scalar::MaxTypedArrayViewType) // DataViews.
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
return js::Scalar::byteSize(elemType);
|
|
||||||
}
|
|
||||||
|
|
||||||
Maybe<Range<const uint8_t>> GetRangeFromView(const dom::ArrayBufferView& view,
|
|
||||||
GLuint elemOffset,
|
GLuint elemOffset,
|
||||||
GLuint elemCountOverride) {
|
GLuint elemCountOverride) {
|
||||||
const auto byteRange = MakeRangeAbv(view); // In bytes.
|
const auto byteRange = Range(data); // In bytes.
|
||||||
const auto bytesPerElem = SizeOfViewElem(view);
|
|
||||||
|
|
||||||
auto elemCount = byteRange.length() / bytesPerElem;
|
auto elemCount = byteRange.length() / bytesPerElem;
|
||||||
if (elemOffset > elemCount) return {};
|
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;
|
bool isDataUpload = false;
|
||||||
auto desc = [&]() -> Maybe<webgl::TexUnpackBlobDesc> {
|
auto desc = [&]() -> Maybe<webgl::TexUnpackBlobDesc> {
|
||||||
@ -4240,17 +4280,23 @@ void ClientWebGLContext::TexImage(uint8_t funcDims, GLenum imageTarget,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto range = GetRangeFromView(view, src.mViewElemOffset,
|
return view.ProcessData(
|
||||||
src.mViewElemLengthOverride);
|
[&](const Span<uint8_t>& aData,
|
||||||
if (!range) {
|
JS::AutoCheckCannotGC&& nogc) -> Maybe<webgl::TexUnpackBlobDesc> {
|
||||||
EnqueueError(LOCAL_GL_INVALID_OPERATION, "`source` too small.");
|
const auto range = GetRangeFromData(aData, SizeOfViewElem(view),
|
||||||
return {};
|
src.mViewElemOffset,
|
||||||
}
|
src.mViewElemLengthOverride);
|
||||||
return Some(webgl::TexUnpackBlobDesc{imageTarget,
|
if (!range) {
|
||||||
size.value(),
|
nogc.reset();
|
||||||
gfxAlphaType::NonPremult,
|
EnqueueError(LOCAL_GL_INVALID_OPERATION, "`source` too small.");
|
||||||
Some(RawBuffer<>{*range}),
|
return {};
|
||||||
{}});
|
}
|
||||||
|
return Some(webgl::TexUnpackBlobDesc{imageTarget,
|
||||||
|
size.value(),
|
||||||
|
gfxAlphaType::NonPremult,
|
||||||
|
Some(RawBuffer<>{*range}),
|
||||||
|
{}});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (src.mImageBitmap) {
|
if (src.mImageBitmap) {
|
||||||
@ -4260,50 +4306,59 @@ void ClientWebGLContext::TexImage(uint8_t funcDims, GLenum imageTarget,
|
|||||||
|
|
||||||
if (src.mImageData) {
|
if (src.mImageData) {
|
||||||
const auto& imageData = *src.mImageData;
|
const auto& imageData = *src.mImageData;
|
||||||
|
dom::Uint8ClampedArray scopedArr;
|
||||||
MOZ_RELEASE_ASSERT(scopedArr.Init(imageData.GetDataObject()));
|
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,
|
|
||||||
"ImageData.data.buffer is Detached. (Maybe you Transfered "
|
|
||||||
"it to a Worker?");
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
// -
|
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 {};
|
||||||
|
}
|
||||||
|
|
||||||
const gfx::IntSize imageSize(imageData.Width(), imageData.Height());
|
// -
|
||||||
const auto sizeFromDims =
|
|
||||||
CheckedInt<size_t>(imageSize.width) * imageSize.height * 4;
|
|
||||||
MOZ_RELEASE_ASSERT(sizeFromDims.isValid() &&
|
|
||||||
sizeFromDims.value() == dataSize);
|
|
||||||
|
|
||||||
const RefPtr<gfx::DataSourceSurface> surf =
|
const gfx::IntSize imageSize(imageData.Width(), imageData.Height());
|
||||||
gfx::Factory::CreateWrappingDataSourceSurface(
|
const auto sizeFromDims =
|
||||||
data, imageSize.width * 4, imageSize,
|
CheckedInt<size_t>(imageSize.width) * imageSize.height * 4;
|
||||||
gfx::SurfaceFormat::R8G8B8A8);
|
MOZ_RELEASE_ASSERT(sizeFromDims.isValid() &&
|
||||||
MOZ_ASSERT(surf);
|
sizeFromDims.value() == dataSize);
|
||||||
|
|
||||||
// -
|
const RefPtr<gfx::DataSourceSurface> surf =
|
||||||
|
gfx::Factory::CreateWrappingDataSourceSurface(
|
||||||
|
data, imageSize.width * 4, imageSize,
|
||||||
|
gfx::SurfaceFormat::R8G8B8A8);
|
||||||
|
MOZ_ASSERT(surf);
|
||||||
|
|
||||||
const auto imageUSize = *uvec2::FromSize(imageSize);
|
// -
|
||||||
const auto concreteSize =
|
|
||||||
size.valueOr(uvec3{imageUSize.x, imageUSize.y, 1});
|
|
||||||
|
|
||||||
// WhatWG "HTML Living Standard" (30 October 2015):
|
const auto imageUSize = *uvec2::FromSize(imageSize);
|
||||||
// "The getImageData(sx, sy, sw, sh) method [...] Pixels must be returned
|
const auto concreteSize =
|
||||||
// as non-premultiplied alpha values."
|
size.valueOr(uvec3{imageUSize.x, imageUSize.y, 1});
|
||||||
return Some(webgl::TexUnpackBlobDesc{imageTarget,
|
|
||||||
concreteSize,
|
// WhatWG "HTML Living Standard" (30 October 2015):
|
||||||
gfxAlphaType::NonPremult,
|
// "The getImageData(sx, sy, sw, sh) method [...] Pixels must be
|
||||||
{},
|
// returned as non-premultiplied alpha values."
|
||||||
{},
|
auto result =
|
||||||
Some(imageUSize),
|
Some(webgl::TexUnpackBlobDesc{imageTarget,
|
||||||
nullptr,
|
concreteSize,
|
||||||
{},
|
gfxAlphaType::NonPremult,
|
||||||
surf});
|
{},
|
||||||
|
{},
|
||||||
|
Some(imageUSize),
|
||||||
|
nullptr,
|
||||||
|
{},
|
||||||
|
surf});
|
||||||
|
nogc.reset(); // Done with aData
|
||||||
|
return result;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (src.mOffscreenCanvas) {
|
if (src.mOffscreenCanvas) {
|
||||||
@ -4563,29 +4618,40 @@ void ClientWebGLContext::CompressedTexImage(bool sub, uint8_t funcDims,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
RawBuffer<> range;
|
|
||||||
Maybe<uint64_t> pboOffset;
|
|
||||||
if (src.mView) {
|
if (src.mView) {
|
||||||
const auto maybe = GetRangeFromView(*src.mView, src.mViewElemOffset,
|
src.mView->ProcessData([&](const Span<uint8_t>& aData,
|
||||||
src.mViewElemLengthOverride);
|
JS::AutoCheckCannotGC&& aNoGC) {
|
||||||
if (!maybe) {
|
const auto range =
|
||||||
EnqueueError(LOCAL_GL_INVALID_VALUE, "`source` too small.");
|
GetRangeFromData(aData, SizeOfViewElem(*src.mView),
|
||||||
|
src.mViewElemOffset, src.mViewElemLengthOverride);
|
||||||
|
if (!range) {
|
||||||
|
aNoGC.reset();
|
||||||
|
EnqueueError(LOCAL_GL_INVALID_VALUE, "`source` too small.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
}
|
});
|
||||||
range = RawBuffer<>{*maybe};
|
return;
|
||||||
} else if (src.mPboOffset) {
|
}
|
||||||
if (!ValidateNonNegative("offset", *src.mPboOffset)) return;
|
if (!src.mPboOffset) {
|
||||||
pboOffset = Some(*src.mPboOffset);
|
|
||||||
} else {
|
|
||||||
MOZ_CRASH("impossible");
|
MOZ_CRASH("impossible");
|
||||||
}
|
}
|
||||||
|
if (!ValidateNonNegative("offset", *src.mPboOffset)) {
|
||||||
// We don't need to shrink `range` because valid calls require `range` to
|
return;
|
||||||
// match requirements exactly.
|
}
|
||||||
|
|
||||||
Run<RPROC(CompressedTexImage)>(
|
Run<RPROC(CompressedTexImage)>(
|
||||||
sub, imageTarget, static_cast<uint32_t>(level), format, CastUvec3(offset),
|
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,
|
void ClientWebGLContext::CopyTexImage(uint8_t funcDims, GLenum imageTarget,
|
||||||
@ -4762,13 +4828,21 @@ void ClientWebGLContext::UniformData(const GLenum funcElemType,
|
|||||||
const WebGLUniformLocationJS* const loc,
|
const WebGLUniformLocationJS* const loc,
|
||||||
bool transpose,
|
bool transpose,
|
||||||
const Range<const uint8_t>& bytes,
|
const Range<const uint8_t>& bytes,
|
||||||
|
JS::AutoCheckCannotGC&& nogc,
|
||||||
GLuint elemOffset,
|
GLuint elemOffset,
|
||||||
GLuint elemCountOverride) const {
|
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");
|
const FuncScope funcScope(*this, "uniform setter");
|
||||||
if (IsContextLost()) return;
|
if (IsContextLost()) {
|
||||||
|
nogc.reset();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const auto& activeLinkResult = GetActiveLinkResult();
|
const auto& activeLinkResult = GetActiveLinkResult();
|
||||||
if (!activeLinkResult) {
|
if (!activeLinkResult) {
|
||||||
|
nogc.reset();
|
||||||
EnqueueError(LOCAL_GL_INVALID_OPERATION, "No active linked Program.");
|
EnqueueError(LOCAL_GL_INVALID_OPERATION, "No active linked Program.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -4777,12 +4851,14 @@ void ClientWebGLContext::UniformData(const GLenum funcElemType,
|
|||||||
|
|
||||||
auto availCount = bytes.length() / sizeof(float);
|
auto availCount = bytes.length() / sizeof(float);
|
||||||
if (elemOffset > availCount) {
|
if (elemOffset > availCount) {
|
||||||
|
nogc.reset();
|
||||||
EnqueueError(LOCAL_GL_INVALID_VALUE, "`elemOffset` too large for `data`.");
|
EnqueueError(LOCAL_GL_INVALID_VALUE, "`elemOffset` too large for `data`.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
availCount -= elemOffset;
|
availCount -= elemOffset;
|
||||||
if (elemCountOverride) {
|
if (elemCountOverride) {
|
||||||
if (elemCountOverride > availCount) {
|
if (elemCountOverride > availCount) {
|
||||||
|
nogc.reset();
|
||||||
EnqueueError(LOCAL_GL_INVALID_VALUE,
|
EnqueueError(LOCAL_GL_INVALID_VALUE,
|
||||||
"`elemCountOverride` too large for `data`.");
|
"`elemCountOverride` too large for `data`.");
|
||||||
return;
|
return;
|
||||||
@ -4794,6 +4870,7 @@ void ClientWebGLContext::UniformData(const GLenum funcElemType,
|
|||||||
|
|
||||||
const auto channels = ElemTypeComponents(funcElemType);
|
const auto channels = ElemTypeComponents(funcElemType);
|
||||||
if (!availCount || availCount % channels != 0) {
|
if (!availCount || availCount % channels != 0) {
|
||||||
|
nogc.reset();
|
||||||
EnqueueError(LOCAL_GL_INVALID_VALUE,
|
EnqueueError(LOCAL_GL_INVALID_VALUE,
|
||||||
"`values` length (%u) must be a positive "
|
"`values` length (%u) must be a positive "
|
||||||
"integer multiple of size of %s.",
|
"integer multiple of size of %s.",
|
||||||
@ -4806,12 +4883,16 @@ void ClientWebGLContext::UniformData(const GLenum funcElemType,
|
|||||||
uint32_t locId = -1;
|
uint32_t locId = -1;
|
||||||
if (MOZ_LIKELY(loc)) {
|
if (MOZ_LIKELY(loc)) {
|
||||||
locId = loc->mLocation;
|
locId = loc->mLocation;
|
||||||
if (!loc->ValidateUsable(*this, "location")) return;
|
if (!loc->ValidateUsable(*this, "location")) {
|
||||||
|
nogc.reset();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// -
|
// -
|
||||||
|
|
||||||
const auto& reqLinkInfo = loc->mParent.lock();
|
const auto& reqLinkInfo = loc->mParent.lock();
|
||||||
if (reqLinkInfo.get() != activeLinkResult) {
|
if (reqLinkInfo.get() != activeLinkResult) {
|
||||||
|
nogc.reset();
|
||||||
EnqueueError(LOCAL_GL_INVALID_OPERATION,
|
EnqueueError(LOCAL_GL_INVALID_OPERATION,
|
||||||
"UniformLocation is not from the current active Program.");
|
"UniformLocation is not from the current active Program.");
|
||||||
return;
|
return;
|
||||||
@ -4831,6 +4912,7 @@ void ClientWebGLContext::UniformData(const GLenum funcElemType,
|
|||||||
}
|
}
|
||||||
validSetters.pop_back(); // Cheekily discard the extra trailing '/'.
|
validSetters.pop_back(); // Cheekily discard the extra trailing '/'.
|
||||||
|
|
||||||
|
nogc.reset();
|
||||||
EnqueueError(LOCAL_GL_INVALID_OPERATION,
|
EnqueueError(LOCAL_GL_INVALID_OPERATION,
|
||||||
"Uniform's `type` requires uniform setter of type %s.",
|
"Uniform's `type` requires uniform setter of type %s.",
|
||||||
validSetters.c_str());
|
validSetters.c_str());
|
||||||
@ -4844,7 +4926,8 @@ void ClientWebGLContext::UniformData(const GLenum funcElemType,
|
|||||||
reinterpret_cast<const webgl::UniformDataVal*>(bytes.begin().get()) +
|
reinterpret_cast<const webgl::UniformDataVal*>(bytes.begin().get()) +
|
||||||
elemOffset;
|
elemOffset;
|
||||||
const auto range = Range{begin, availCount};
|
const auto range = Range{begin, availCount};
|
||||||
Run<RPROC(UniformData)>(locId, transpose, RawBuffer{range});
|
RunWithGCData<RPROC(UniformData)>(std::move(nogc), locId, transpose,
|
||||||
|
RawBuffer{range});
|
||||||
}
|
}
|
||||||
|
|
||||||
// -
|
// -
|
||||||
@ -5044,21 +5127,20 @@ void ClientWebGLContext::ReadPixels(GLint x, GLint y, GLsizei width,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t* bytes;
|
size_t elemSize = SizeOfViewElem(dstData);
|
||||||
size_t byteLen;
|
dstData.ProcessFixedData([&](const Span<uint8_t>& aData) {
|
||||||
if (!ValidateArrayBufferView(dstData, dstElemOffset, 0,
|
const auto& range = ValidateArrayBufferView(aData, elemSize, dstElemOffset,
|
||||||
LOCAL_GL_INVALID_VALUE, &bytes, &byteLen)) {
|
0, LOCAL_GL_INVALID_VALUE);
|
||||||
return;
|
if (!range) {
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const auto desc = webgl::ReadPixelsDesc{{x, y},
|
const auto desc = webgl::ReadPixelsDesc{{x, y},
|
||||||
*uvec2::From(width, height),
|
*uvec2::From(width, height),
|
||||||
{format, type},
|
{format, type},
|
||||||
state.mPixelPackState};
|
state.mPixelPackState};
|
||||||
const auto range = Range<uint8_t>(bytes, byteLen);
|
(void)DoReadPixels(desc, *range);
|
||||||
if (!DoReadPixels(desc, range)) {
|
});
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ClientWebGLContext::DoReadPixels(const webgl::ReadPixelsDesc& desc,
|
bool ClientWebGLContext::DoReadPixels(const webgl::ReadPixelsDesc& desc,
|
||||||
@ -6566,34 +6648,26 @@ const webgl::LinkResult& ClientWebGLContext::GetLinkResult(
|
|||||||
|
|
||||||
// ---------------------------
|
// ---------------------------
|
||||||
|
|
||||||
bool ClientWebGLContext::ValidateArrayBufferView(
|
Maybe<Range<uint8_t>> ClientWebGLContext::ValidateArrayBufferView(
|
||||||
const dom::ArrayBufferView& view, GLuint elemOffset,
|
const Span<uint8_t>& bytes, size_t elemSize, GLuint elemOffset,
|
||||||
GLuint elemCountOverride, const GLenum errorEnum, uint8_t** const out_bytes,
|
GLuint elemCountOverride, const GLenum errorEnum) const {
|
||||||
size_t* const out_byteLen) const {
|
size_t elemCount = bytes.Length() / elemSize;
|
||||||
view.ComputeState();
|
|
||||||
uint8_t* const bytes = view.Data();
|
|
||||||
const size_t byteLen = view.Length();
|
|
||||||
|
|
||||||
const auto& elemSize = SizeOfViewElem(view);
|
|
||||||
|
|
||||||
size_t elemCount = byteLen / elemSize;
|
|
||||||
if (elemOffset > elemCount) {
|
if (elemOffset > elemCount) {
|
||||||
EnqueueError(errorEnum, "Invalid offset into ArrayBufferView.");
|
EnqueueError(errorEnum, "Invalid offset into ArrayBufferView.");
|
||||||
return false;
|
return Nothing();
|
||||||
}
|
}
|
||||||
elemCount -= elemOffset;
|
elemCount -= elemOffset;
|
||||||
|
|
||||||
if (elemCountOverride) {
|
if (elemCountOverride) {
|
||||||
if (elemCountOverride > elemCount) {
|
if (elemCountOverride > elemCount) {
|
||||||
EnqueueError(errorEnum, "Invalid sub-length for ArrayBufferView.");
|
EnqueueError(errorEnum, "Invalid sub-length for ArrayBufferView.");
|
||||||
return false;
|
return Nothing();
|
||||||
}
|
}
|
||||||
elemCount = elemCountOverride;
|
elemCount = elemCountOverride;
|
||||||
}
|
}
|
||||||
|
|
||||||
*out_bytes = bytes + (elemOffset * elemSize);
|
return Some(Range<uint8_t>(
|
||||||
*out_byteLen = elemCount * elemSize;
|
bytes.Subspan(elemOffset * elemSize, elemCount * elemSize)));
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------
|
// ---------------------------
|
||||||
|
@ -7,9 +7,11 @@
|
|||||||
#define CLIENTWEBGLCONTEXT_H_
|
#define CLIENTWEBGLCONTEXT_H_
|
||||||
|
|
||||||
#include "GLConsts.h"
|
#include "GLConsts.h"
|
||||||
|
#include "js/GCAPI.h"
|
||||||
#include "mozilla/dom/ImageData.h"
|
#include "mozilla/dom/ImageData.h"
|
||||||
#include "mozilla/Range.h"
|
#include "mozilla/Range.h"
|
||||||
#include "mozilla/RefCounted.h"
|
#include "mozilla/RefCounted.h"
|
||||||
|
#include "mozilla/dom/TypedArray.h"
|
||||||
#include "nsICanvasRenderingContextInternal.h"
|
#include "nsICanvasRenderingContextInternal.h"
|
||||||
#include "nsWrapperCache.h"
|
#include "nsWrapperCache.h"
|
||||||
#include "mozilla/dom/WebGLRenderingContextBinding.h"
|
#include "mozilla/dom/WebGLRenderingContextBinding.h"
|
||||||
@ -24,6 +26,7 @@
|
|||||||
#include "WebGLCommandQueue.h"
|
#include "WebGLCommandQueue.h"
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <type_traits>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@ -618,22 +621,47 @@ using Float32ListU = dom::MaybeSharedFloat32ArrayOrUnrestrictedFloatSequence;
|
|||||||
using Int32ListU = dom::MaybeSharedInt32ArrayOrLongSequence;
|
using Int32ListU = dom::MaybeSharedInt32ArrayOrLongSequence;
|
||||||
using Uint32ListU = dom::MaybeSharedUint32ArrayOrUnsignedLongSequence;
|
using Uint32ListU = dom::MaybeSharedUint32ArrayOrUnsignedLongSequence;
|
||||||
|
|
||||||
inline Range<const float> MakeRange(const Float32ListU& list) {
|
template <typename Converter, typename T>
|
||||||
if (list.IsFloat32Array()) return MakeRangeAbv(list.GetAsFloat32Array());
|
inline bool ConvertSequence(const dom::Sequence<T>& sequence,
|
||||||
|
Converter&& converter) {
|
||||||
return MakeRange(list.GetAsUnrestrictedFloatSequence());
|
// 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) {
|
template <typename Converter>
|
||||||
if (list.IsInt32Array()) return MakeRangeAbv(list.GetAsInt32Array());
|
inline bool Convert(const Float32ListU& list, Converter&& converter) {
|
||||||
|
if (list.IsFloat32Array()) {
|
||||||
|
return list.GetAsFloat32Array().ProcessData(
|
||||||
|
std::forward<Converter>(converter));
|
||||||
|
}
|
||||||
|
|
||||||
return MakeRange(list.GetAsLongSequence());
|
return ConvertSequence(list.GetAsUnrestrictedFloatSequence(),
|
||||||
|
std::forward<Converter>(converter));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline Range<const uint32_t> MakeRange(const Uint32ListU& list) {
|
template <typename Converter>
|
||||||
if (list.IsUint32Array()) return MakeRangeAbv(list.GetAsUint32Array());
|
inline bool Convert(const Int32ListU& list, Converter&& converter) {
|
||||||
|
if (list.IsInt32Array()) {
|
||||||
|
return list.GetAsInt32Array().ProcessData(
|
||||||
|
std::forward<Converter>(converter));
|
||||||
|
}
|
||||||
|
|
||||||
return MakeRange(list.GetAsUnsignedLongSequence());
|
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>
|
template <typename T>
|
||||||
@ -644,6 +672,11 @@ inline Range<const uint8_t> MakeByteRange(const T& x) {
|
|||||||
typed.length() * sizeof(typed[0]));
|
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 {
|
struct TexImageSourceAdapter final : public TexImageSource {
|
||||||
@ -898,11 +931,11 @@ class ClientWebGLContext final : public nsICanvasRenderingContextInternal,
|
|||||||
void EnqueueErrorImpl(GLenum errorOrZero, const nsACString&) const;
|
void EnqueueErrorImpl(GLenum errorOrZero, const nsACString&) const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
bool ValidateArrayBufferView(const dom::ArrayBufferView& view,
|
Maybe<Range<uint8_t>> ValidateArrayBufferView(const Span<uint8_t>& bytes,
|
||||||
GLuint elemOffset, GLuint elemCountOverride,
|
size_t elemSize,
|
||||||
const GLenum errorEnum,
|
GLuint elemOffset,
|
||||||
uint8_t** const out_bytes,
|
GLuint elemCountOverride,
|
||||||
size_t* const out_byteLen) const;
|
const GLenum errorEnum) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
template <typename T>
|
template <typename T>
|
||||||
@ -1297,24 +1330,48 @@ class ClientWebGLContext final : public nsICanvasRenderingContextInternal,
|
|||||||
// -
|
// -
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void ClearBufferTv(GLenum buffer, GLint drawBuffer, webgl::AttribBaseType,
|
void ClearBufferTv(const GLenum buffer, const GLint drawBuffer,
|
||||||
const Range<const uint8_t>& view, GLuint srcElemOffset);
|
const webgl::AttribBaseType type,
|
||||||
|
JS::AutoCheckCannotGC&& nogc,
|
||||||
|
const Span<const uint8_t>& view,
|
||||||
|
const GLuint srcElemOffset);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void ClearBufferfv(GLenum buffer, GLint drawBuffer, const Float32ListU& list,
|
void ClearBufferfv(GLenum buffer, GLint drawBuffer, const Float32ListU& list,
|
||||||
GLuint srcElemOffset) {
|
GLuint srcElemOffset) {
|
||||||
ClearBufferTv(buffer, drawBuffer, webgl::AttribBaseType::Float,
|
const FuncScope funcScope(*this, "clearBufferfv");
|
||||||
MakeByteRange(list), srcElemOffset);
|
if (!Convert(list, [&](const Span<const float>& aData,
|
||||||
|
JS::AutoCheckCannotGC&& nogc) {
|
||||||
|
ClearBufferTv(buffer, drawBuffer, webgl::AttribBaseType::Float,
|
||||||
|
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,
|
void ClearBufferiv(GLenum buffer, GLint drawBuffer, const Int32ListU& list,
|
||||||
GLuint srcElemOffset) {
|
GLuint srcElemOffset) {
|
||||||
ClearBufferTv(buffer, drawBuffer, webgl::AttribBaseType::Int,
|
const FuncScope funcScope(*this, "clearBufferiv");
|
||||||
MakeByteRange(list), srcElemOffset);
|
if (!Convert(list, [&](const Span<const int32_t>& aData,
|
||||||
|
JS::AutoCheckCannotGC&& nogc) {
|
||||||
|
ClearBufferTv(buffer, drawBuffer, webgl::AttribBaseType::Int,
|
||||||
|
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,
|
void ClearBufferuiv(GLenum buffer, GLint drawBuffer, const Uint32ListU& list,
|
||||||
GLuint srcElemOffset) {
|
GLuint srcElemOffset) {
|
||||||
ClearBufferTv(buffer, drawBuffer, webgl::AttribBaseType::Uint,
|
const FuncScope funcScope(*this, "clearBufferuiv");
|
||||||
MakeByteRange(list), srcElemOffset);
|
if (!Convert(list, [&](const Span<const uint32_t>& aData,
|
||||||
|
JS::AutoCheckCannotGC&& nogc) {
|
||||||
|
ClearBufferTv(buffer, drawBuffer, webgl::AttribBaseType::Uint,
|
||||||
|
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();
|
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,
|
void UniformData(GLenum funcElemType, const WebGLUniformLocationJS* const loc,
|
||||||
bool transpose, const Range<const uint8_t>& bytes,
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
// -
|
// -
|
||||||
|
|
||||||
@ -1898,12 +1972,15 @@ class ClientWebGLContext final : public nsICanvasRenderingContextInternal,
|
|||||||
|
|
||||||
// -
|
// -
|
||||||
|
|
||||||
#define _(NT, TypeListU, TYPE) \
|
#define _(NT, TypeListU, TYPE) \
|
||||||
void Uniform##NT##v(const WebGLUniformLocationJS* const loc, \
|
void Uniform##NT##v(const WebGLUniformLocationJS* const loc, \
|
||||||
const TypeListU& list, GLuint elemOffset = 0, \
|
const TypeListU& list, GLuint elemOffset = 0, \
|
||||||
GLuint elemCountOverride = 0) const { \
|
GLuint elemCountOverride = 0) const { \
|
||||||
UniformData(TYPE, loc, false, MakeByteRange(list), elemOffset, \
|
Convert(list, [&](const auto& aData, JS::AutoCheckCannotGC&& nogc) { \
|
||||||
elemCountOverride); \
|
UniformData(TYPE, loc, false, MakeByteRange(aData), std::move(nogc), \
|
||||||
|
elemOffset, elemCountOverride); \
|
||||||
|
return true; \
|
||||||
|
}); \
|
||||||
}
|
}
|
||||||
|
|
||||||
_(1f, Float32ListU, LOCAL_GL_FLOAT)
|
_(1f, Float32ListU, LOCAL_GL_FLOAT)
|
||||||
@ -1927,8 +2004,12 @@ class ClientWebGLContext final : public nsICanvasRenderingContextInternal,
|
|||||||
void UniformMatrix##X##fv(const WebGLUniformLocationJS* loc, bool transpose, \
|
void UniformMatrix##X##fv(const WebGLUniformLocationJS* loc, bool transpose, \
|
||||||
const Float32ListU& list, GLuint elemOffset = 0, \
|
const Float32ListU& list, GLuint elemOffset = 0, \
|
||||||
GLuint elemCountOverride = 0) const { \
|
GLuint elemCountOverride = 0) const { \
|
||||||
UniformData(LOCAL_GL_FLOAT_MAT##X, loc, transpose, MakeByteRange(list), \
|
Convert(list, [&](const Span<const float>& aData, \
|
||||||
elemOffset, elemCountOverride); \
|
JS::AutoCheckCannotGC&& nogc) { \
|
||||||
|
UniformData(LOCAL_GL_FLOAT_MAT##X, loc, transpose, MakeByteRange(aData), \
|
||||||
|
std::move(nogc), elemOffset, elemCountOverride); \
|
||||||
|
return true; \
|
||||||
|
}); \
|
||||||
}
|
}
|
||||||
|
|
||||||
_(2)
|
_(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) {
|
void VertexAttrib1fv(const GLuint index, const Float32ListU& list) {
|
||||||
const FuncScope funcScope(*this, "vertexAttrib1fv");
|
const FuncScope funcScope(*this, "vertexAttrib1fv");
|
||||||
if (IsContextLost()) return;
|
if (IsContextLost()) return;
|
||||||
|
|
||||||
const auto range = MakeRange(list);
|
float arr[1];
|
||||||
if (range.length() < 1) {
|
if (!MakeArrayFromList(list, arr)) {
|
||||||
EnqueueError(LOCAL_GL_INVALID_VALUE, "Length of `list` must be >=1.");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
VertexAttrib1f(index, arr[0]);
|
||||||
VertexAttrib1f(index, range[0]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VertexAttrib2fv(const GLuint index, const Float32ListU& list) {
|
void VertexAttrib2fv(const GLuint index, const Float32ListU& list) {
|
||||||
const FuncScope funcScope(*this, "vertexAttrib1fv");
|
const FuncScope funcScope(*this, "vertexAttrib1fv");
|
||||||
if (IsContextLost()) return;
|
if (IsContextLost()) return;
|
||||||
|
|
||||||
const auto range = MakeRange(list);
|
float arr[2];
|
||||||
if (range.length() < 2) {
|
if (!MakeArrayFromList(list, arr)) {
|
||||||
EnqueueError(LOCAL_GL_INVALID_VALUE, "Length of `list` must be >=2.");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
VertexAttrib2f(index, arr[0], arr[1]);
|
||||||
VertexAttrib2f(index, range[0], range[1]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VertexAttrib3fv(const GLuint index, const Float32ListU& list) {
|
void VertexAttrib3fv(const GLuint index, const Float32ListU& list) {
|
||||||
const FuncScope funcScope(*this, "vertexAttrib1fv");
|
const FuncScope funcScope(*this, "vertexAttrib1fv");
|
||||||
if (IsContextLost()) return;
|
if (IsContextLost()) return;
|
||||||
|
|
||||||
const auto range = MakeRange(list);
|
float arr[3];
|
||||||
if (range.length() < 3) {
|
if (!MakeArrayFromList(list, arr)) {
|
||||||
EnqueueError(LOCAL_GL_INVALID_VALUE, "Length of `list` must be >=3.");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
VertexAttrib3f(index, arr[0], arr[1], arr[2]);
|
||||||
VertexAttrib3f(index, range[0], range[1], range[2]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VertexAttrib4fv(GLuint index, const Float32ListU& list) {
|
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) {
|
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) {
|
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) {
|
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>
|
template <typename MethodType, MethodType method, typename... Args>
|
||||||
void Run(Args&&... aArgs) const;
|
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
|
// Helpers for DOM operations, composition, actors, etc
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
|
@ -1337,7 +1337,6 @@ already_AddRefed<ImageBitmap> ImageBitmap::CreateInternal(
|
|||||||
"failed?)");
|
"failed?)");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
array.ComputeState();
|
|
||||||
const SurfaceFormat FORMAT = SurfaceFormat::R8G8B8A8;
|
const SurfaceFormat FORMAT = SurfaceFormat::R8G8B8A8;
|
||||||
// ImageData's underlying data is not alpha-premultiplied.
|
// ImageData's underlying data is not alpha-premultiplied.
|
||||||
auto alphaType = (aOptions.mPremultiplyAlpha == PremultiplyAlpha::Premultiply)
|
auto alphaType = (aOptions.mPremultiplyAlpha == PremultiplyAlpha::Premultiply)
|
||||||
@ -1348,7 +1347,6 @@ already_AddRefed<ImageBitmap> ImageBitmap::CreateInternal(
|
|||||||
const uint32_t imageWidth = aImageData.Width();
|
const uint32_t imageWidth = aImageData.Width();
|
||||||
const uint32_t imageHeight = aImageData.Height();
|
const uint32_t imageHeight = aImageData.Height();
|
||||||
const uint32_t imageStride = imageWidth * BYTES_PER_PIXEL;
|
const uint32_t imageStride = imageWidth * BYTES_PER_PIXEL;
|
||||||
const uint32_t dataLength = array.Length();
|
|
||||||
const gfx::IntSize imageSize(imageWidth, imageHeight);
|
const gfx::IntSize imageSize(imageWidth, imageHeight);
|
||||||
|
|
||||||
// Check the ImageData is neutered or not.
|
// Check the ImageData is neutered or not.
|
||||||
@ -1357,52 +1355,46 @@ already_AddRefed<ImageBitmap> ImageBitmap::CreateInternal(
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((imageWidth * imageHeight * BYTES_PER_PIXEL) != dataLength) {
|
return array.ProcessFixedData(
|
||||||
aRv.ThrowInvalidStateError("Data size / image format mismatch");
|
[&](const Span<const uint8_t>& aData) -> already_AddRefed<ImageBitmap> {
|
||||||
return nullptr;
|
const uint32_t dataLength = aData.Length();
|
||||||
}
|
if ((imageWidth * imageHeight * BYTES_PER_PIXEL) != dataLength) {
|
||||||
|
aRv.ThrowInvalidStateError("Data size / image format mismatch");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
// Create and Crop the raw data into a layers::Image
|
// Create and Crop the raw data into a layers::Image
|
||||||
RefPtr<layers::Image> data;
|
RefPtr<layers::Image> data;
|
||||||
|
|
||||||
// If the data could move during a GC, copy it out into a local buffer that
|
uint8_t* fixedData = const_cast<uint8_t*>(aData.Elements());
|
||||||
// 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
|
if (NS_IsMainThread()) {
|
||||||
// `array` was using (safe because the data buffer is fixed, and the holding
|
data =
|
||||||
// JSObject is being kept alive elsewhere.)
|
CreateImageFromRawData(imageSize, imageStride, FORMAT, fixedData,
|
||||||
array.Reset();
|
dataLength, aCropRect, aOptions);
|
||||||
|
} else {
|
||||||
|
RefPtr<CreateImageFromRawDataInMainThreadSyncTask> task =
|
||||||
|
new CreateImageFromRawDataInMainThreadSyncTask(
|
||||||
|
fixedData, dataLength, imageStride, FORMAT, imageSize,
|
||||||
|
aCropRect, getter_AddRefs(data), aOptions);
|
||||||
|
task->Dispatch(Canceling, aRv);
|
||||||
|
}
|
||||||
|
|
||||||
if (NS_IsMainThread()) {
|
if (NS_WARN_IF(!data)) {
|
||||||
data = CreateImageFromRawData(imageSize, imageStride, FORMAT, fixedData,
|
aRv.ThrowInvalidStateError("Failed to create internal image");
|
||||||
dataLength, aCropRect, aOptions);
|
return nullptr;
|
||||||
} else {
|
}
|
||||||
RefPtr<CreateImageFromRawDataInMainThreadSyncTask> task =
|
|
||||||
new CreateImageFromRawDataInMainThreadSyncTask(
|
|
||||||
fixedData, dataLength, imageStride, FORMAT, imageSize, aCropRect,
|
|
||||||
getter_AddRefs(data), aOptions);
|
|
||||||
task->Dispatch(Canceling, aRv);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (NS_WARN_IF(!data)) {
|
// Create an ImageBitmap.
|
||||||
aRv.ThrowInvalidStateError("Failed to create internal image");
|
RefPtr<ImageBitmap> ret =
|
||||||
return nullptr;
|
new ImageBitmap(aGlobal, data, false /* write-only */, alphaType);
|
||||||
}
|
ret->mAllocatedImageData = true;
|
||||||
|
|
||||||
// Create an ImageBitmap.
|
// The cropping information has been handled in the
|
||||||
RefPtr<ImageBitmap> ret =
|
// CreateImageFromRawData() function.
|
||||||
new ImageBitmap(aGlobal, data, false /* write-only */, alphaType);
|
|
||||||
|
|
||||||
ret->mAllocatedImageData = true;
|
return ret.forget();
|
||||||
|
});
|
||||||
// The cropping information has been handled in the CreateImageFromRawData()
|
|
||||||
// function.
|
|
||||||
|
|
||||||
return ret.forget();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* static */
|
/* static */
|
||||||
|
@ -51,8 +51,7 @@ already_AddRefed<ImageData> ImageData::Constructor(const GlobalObject& aGlobal,
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Restrict the typed array length to INT32_MAX because that's all we support
|
// Restrict the typed array length to INT32_MAX because that's all we support.
|
||||||
// in dom::TypedArray::ComputeState.
|
|
||||||
CheckedInt<uint32_t> length = CheckedInt<uint32_t>(aWidth) * aHeight * 4;
|
CheckedInt<uint32_t> length = CheckedInt<uint32_t>(aWidth) * aHeight * 4;
|
||||||
if (!length.isValid() || length.value() > INT32_MAX) {
|
if (!length.isValid() || length.value() > INT32_MAX) {
|
||||||
aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
|
aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
|
||||||
@ -73,9 +72,11 @@ already_AddRefed<ImageData> ImageData::Constructor(
|
|||||||
const GlobalObject& aGlobal, const Uint8ClampedArray& aData,
|
const GlobalObject& aGlobal, const Uint8ClampedArray& aData,
|
||||||
const uint32_t aWidth, const Optional<uint32_t>& aHeight,
|
const uint32_t aWidth, const Optional<uint32_t>& aHeight,
|
||||||
ErrorResult& aRv) {
|
ErrorResult& aRv) {
|
||||||
aData.ComputeState();
|
Maybe<uint32_t> maybeLength = aData.ProcessData(
|
||||||
|
[&](const Span<uint8_t>& aData, JS::AutoCheckCannotGC&& nogc) {
|
||||||
uint32_t length = aData.Length();
|
return Some(aData.Length());
|
||||||
|
});
|
||||||
|
uint32_t length = maybeLength.valueOr(0);
|
||||||
if (length == 0 || length % 4) {
|
if (length == 0 || length % 4) {
|
||||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -1108,14 +1108,6 @@ inline Range<const T> MakeRange(const RawBuffer<T>& from) {
|
|||||||
return from.Data();
|
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);
|
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);
|
return range.length() * sizeof(T);
|
||||||
}
|
}
|
||||||
|
|
||||||
Maybe<Range<const uint8_t>> GetRangeFromView(const dom::ArrayBufferView& view,
|
|
||||||
GLuint elemOffset,
|
|
||||||
GLuint elemCountOverride);
|
|
||||||
|
|
||||||
// -
|
// -
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
|
@ -45,7 +45,6 @@ struct RsaHashedKeyAlgorithmStorage {
|
|||||||
aRsa.mModulusLength = mModulusLength;
|
aRsa.mModulusLength = mModulusLength;
|
||||||
aRsa.mHash.mName = mHash.mName;
|
aRsa.mHash.mName = mHash.mName;
|
||||||
aRsa.mPublicExponent.Init(exponent);
|
aRsa.mPublicExponent.Init(exponent);
|
||||||
aRsa.mPublicExponent.ComputeState();
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -104,20 +104,11 @@ void TextDecoder::Decode(const Optional<ArrayBufferViewOrArrayBuffer>& aBuffer,
|
|||||||
DecodeNative(nullptr, aOptions.mStream, aOutDecodedString, aRv);
|
DecodeNative(nullptr, aOptions.mStream, aOutDecodedString, aRv);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const ArrayBufferViewOrArrayBuffer& buf = aBuffer.Value();
|
|
||||||
uint8_t* data;
|
ProcessTypedArrays(aBuffer.Value(), [&](const Span<uint8_t>& aData,
|
||||||
uint32_t length;
|
JS::AutoCheckCannotGC&&) {
|
||||||
if (buf.IsArrayBufferView()) {
|
DecodeNative(aData, aOptions.mStream, aOutDecodedString, aRv);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextDecoderCommon::GetEncoding(nsAString& aEncoding) {
|
void TextDecoderCommon::GetEncoding(nsAString& aEncoding) {
|
||||||
|
@ -46,28 +46,6 @@ JSObject* TextDecoderStream::WrapObject(JSContext* aCx,
|
|||||||
return TextDecoderStream_Binding::Wrap(aCx, this, aGivenProto);
|
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 {
|
class TextDecoderStreamAlgorithms : public TransformerAlgorithmsWrapper {
|
||||||
NS_DECL_ISUPPORTS_INHERITED
|
NS_DECL_ISUPPORTS_INHERITED
|
||||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(TextDecoderStreamAlgorithms,
|
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
|
// Note that the most of the decoding algorithm is implemented in
|
||||||
// mozilla::Decoder, and this is mainly about calling it properly.
|
// mozilla::Decoder, and this is mainly about calling it properly.
|
||||||
// https://encoding.spec.whatwg.org/#decode-and-enqueue-a-chunk
|
// https://encoding.spec.whatwg.org/#decode-and-enqueue-a-chunk
|
||||||
MOZ_CAN_RUN_SCRIPT void DecodeSpanAndEnqueue(
|
// TODO: This does not allow shared array buffers, just as the non-stream
|
||||||
JSContext* aCx, Span<const uint8_t> aInput, bool aFlush,
|
// TextDecoder/Encoder don't. (Bug 1561594)
|
||||||
|
MOZ_CAN_RUN_SCRIPT void DecodeBufferSourceAndEnqueue(
|
||||||
|
JSContext* aCx, OwningArrayBufferViewOrArrayBuffer* aInput, bool aFlush,
|
||||||
TransformStreamDefaultController& aController, ErrorResult& aRv) {
|
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;
|
nsString outDecodedString;
|
||||||
auto output = outDecodedString.GetMutableData(needed.value(), fallible);
|
if (aInput) {
|
||||||
if (!output) {
|
ProcessTypedArrays(*aInput, [&](const Span<const uint8_t>& aData,
|
||||||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
JS::AutoCheckCannotGC&&) {
|
||||||
return;
|
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()) {
|
if (aRv.Failed()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -107,7 +83,7 @@ class TextDecoderStreamAlgorithms : public TransformerAlgorithmsWrapper {
|
|||||||
// Step 4.2. If outputChunk is non-empty, then enqueue outputChunk in
|
// Step 4.2. If outputChunk is non-empty, then enqueue outputChunk in
|
||||||
// decoder’s transform.
|
// decoder’s transform.
|
||||||
JS::Rooted<JS::Value> outputChunk(aCx);
|
JS::Rooted<JS::Value> outputChunk(aCx);
|
||||||
if (!xpc::NonVoidStringToJsval(aCx, outDecodedString, &outputChunk)) {
|
if (!ToJSValue(aCx, outDecodedString, &outputChunk)) {
|
||||||
JS_ClearPendingException(aCx);
|
JS_ClearPendingException(aCx);
|
||||||
aRv.Throw(NS_ERROR_UNEXPECTED);
|
aRv.Throw(NS_ERROR_UNEXPECTED);
|
||||||
return;
|
return;
|
||||||
@ -136,13 +112,14 @@ class TextDecoderStreamAlgorithms : public TransformerAlgorithmsWrapper {
|
|||||||
|
|
||||||
// Step 1. Let bufferSource be the result of converting chunk to an
|
// Step 1. Let bufferSource be the result of converting chunk to an
|
||||||
// [AllowShared] BufferSource.
|
// [AllowShared] BufferSource.
|
||||||
// (But here we get a mozilla::Span instead)
|
RootedUnion<OwningArrayBufferViewOrArrayBuffer> bufferSource(cx);
|
||||||
Span<const uint8_t> input = ExtractSpanFromBufferSource(cx, aChunk, aRv);
|
if (!bufferSource.Init(cx, aChunk)) {
|
||||||
if (aRv.Failed()) {
|
aRv.MightThrowJSException();
|
||||||
|
aRv.StealExceptionFromJSContext(cx);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
DecodeSpanAndEnqueue(cx, input, false, aController, aRv);
|
DecodeBufferSourceAndEnqueue(cx, &bufferSource, false, aController, aRv);
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://encoding.spec.whatwg.org/#dom-textdecoderstream
|
// https://encoding.spec.whatwg.org/#dom-textdecoderstream
|
||||||
@ -162,7 +139,7 @@ class TextDecoderStreamAlgorithms : public TransformerAlgorithmsWrapper {
|
|||||||
// https://encoding.spec.whatwg.org/#flush-and-enqueue
|
// https://encoding.spec.whatwg.org/#flush-and-enqueue
|
||||||
// (The flush and enqueue algorithm is basically a subset of decode and
|
// (The flush and enqueue algorithm is basically a subset of decode and
|
||||||
// enqueue one, so let's reuse it)
|
// enqueue one, so let's reuse it)
|
||||||
DecodeSpanAndEnqueue(cx, Span<const uint8_t>(), true, aController, aRv);
|
DecodeBufferSourceAndEnqueue(cx, nullptr, true, aController, aRv);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -69,9 +69,6 @@ class TextDecoderStream final : public nsISupports,
|
|||||||
RefPtr<TransformStream> mStream;
|
RefPtr<TransformStream> mStream;
|
||||||
};
|
};
|
||||||
|
|
||||||
Span<const uint8_t> ExtractSpanFromBufferSource(
|
|
||||||
JSContext* aCx, JS::Handle<JS::Value> aBufferSource, ErrorResult& aRv);
|
|
||||||
|
|
||||||
} // namespace mozilla::dom
|
} // namespace mozilla::dom
|
||||||
|
|
||||||
#endif // DOM_ENCODING_TEXTDECODERSTREAM_H_
|
#endif // DOM_ENCODING_TEXTDECODERSTREAM_H_
|
||||||
|
@ -30,17 +30,21 @@ void TextEncoder::EncodeInto(JSContext* aCx, JS::Handle<JSString*> aSrc,
|
|||||||
const Uint8Array& aDst,
|
const Uint8Array& aDst,
|
||||||
TextEncoderEncodeIntoResult& aResult,
|
TextEncoderEncodeIntoResult& aResult,
|
||||||
OOMReporter& aError) {
|
OOMReporter& aError) {
|
||||||
aDst.ComputeState();
|
DebugOnly<size_t> dstLength = 0;
|
||||||
size_t read;
|
auto maybe = aDst.ProcessData(
|
||||||
size_t written;
|
[&](const Span<uint8_t>& aData, JS::AutoCheckCannotGC&&) {
|
||||||
auto maybe = JS_EncodeStringToUTF8BufferPartial(
|
dstLength = aData.Length();
|
||||||
aCx, aSrc, AsWritableChars(Span(aDst.Data(), aDst.Length())));
|
return JS_EncodeStringToUTF8BufferPartial(aCx, aSrc,
|
||||||
|
AsWritableChars(aData));
|
||||||
|
});
|
||||||
if (!maybe) {
|
if (!maybe) {
|
||||||
aError.ReportOOM();
|
aError.ReportOOM();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
size_t read;
|
||||||
|
size_t written;
|
||||||
std::tie(read, written) = *maybe;
|
std::tie(read, written) = *maybe;
|
||||||
MOZ_ASSERT(written <= aDst.Length());
|
MOZ_ASSERT(written <= dstLength);
|
||||||
aResult.mRead.Construct() = read;
|
aResult.mRead.Construct() = read;
|
||||||
aResult.mWritten.Construct() = written;
|
aResult.mWritten.Construct() = written;
|
||||||
}
|
}
|
||||||
|
@ -536,24 +536,18 @@ uint64_t FileSystemSyncAccessHandle::ReadOrWrite(
|
|||||||
return 0;
|
return 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
const auto dataSpan = [&aBuffer]() {
|
// Handle seek before read ('at')
|
||||||
if (aBuffer.IsArrayBuffer()) {
|
const auto at = [&aOptions]() -> uint64_t {
|
||||||
const ArrayBuffer& buffer = aBuffer.GetAsArrayBuffer();
|
if (aOptions.mAt.WasPassed()) {
|
||||||
buffer.ComputeState();
|
return aOptions.mAt.Value();
|
||||||
return Span{buffer.Data(), buffer.Length()};
|
|
||||||
}
|
}
|
||||||
MOZ_ASSERT(aBuffer.IsArrayBufferView());
|
// Spec says default for at is 0 (2.6)
|
||||||
const ArrayBufferView& buffer = aBuffer.GetAsArrayBufferView();
|
return 0;
|
||||||
buffer.ComputeState();
|
|
||||||
return Span{buffer.Data(), buffer.Length()};
|
|
||||||
}();
|
}();
|
||||||
|
|
||||||
CheckedInt<int64_t> offset = 0;
|
const auto offset = CheckedInt<int64_t>(at);
|
||||||
if (aOptions.mAt.WasPassed()) {
|
QM_TRY(MOZ_TO_RESULT(offset.isValid()), throwAndReturn);
|
||||||
// Handle seek before read ('at')
|
|
||||||
offset = CheckedInt<int64_t>(aOptions.mAt.Value());
|
|
||||||
QM_TRY(MOZ_TO_RESULT(offset.isValid()), throwAndReturn);
|
|
||||||
}
|
|
||||||
AutoSyncLoopHolder syncLoop(mWorkerRef->Private(), Canceling);
|
AutoSyncLoopHolder syncLoop(mWorkerRef->Private(), Canceling);
|
||||||
|
|
||||||
nsCOMPtr<nsISerialEventTarget> syncLoopTarget =
|
nsCOMPtr<nsISerialEventTarget> syncLoopTarget =
|
||||||
@ -565,72 +559,73 @@ uint64_t FileSystemSyncAccessHandle::ReadOrWrite(
|
|||||||
|
|
||||||
uint64_t totalCount = 0;
|
uint64_t totalCount = 0;
|
||||||
|
|
||||||
InvokeAsync(
|
ProcessTypedArraysFixed(aBuffer, [&](const Span<uint8_t> aData) {
|
||||||
mIOTaskQueue, __func__,
|
InvokeAsync(
|
||||||
[selfHolder = fs::TargetPtrHolder(this), dataSpan,
|
mIOTaskQueue, __func__,
|
||||||
use_offset = aOptions.mAt.WasPassed(), offset, aRead, &totalCount]() {
|
[selfHolder = fs::TargetPtrHolder(this), aData,
|
||||||
QM_TRY(MOZ_TO_RESULT(selfHolder->EnsureStream()),
|
use_offset = aOptions.mAt.WasPassed(), offset, aRead, syncLoopTarget,
|
||||||
CreateAndRejectBoolPromise);
|
&totalCount]() {
|
||||||
|
QM_TRY(MOZ_TO_RESULT(selfHolder->EnsureStream()),
|
||||||
if (use_offset) {
|
|
||||||
LOG_VERBOSE(("%p: Seeking to %" PRIu64, selfHolder->mStream.get(),
|
|
||||||
offset.value()));
|
|
||||||
|
|
||||||
QM_TRY(MOZ_TO_RESULT(selfHolder->mStream->Seek(
|
|
||||||
nsISeekableStream::NS_SEEK_SET, offset.value())),
|
|
||||||
CreateAndRejectBoolPromise);
|
CreateAndRejectBoolPromise);
|
||||||
}
|
if (use_offset) {
|
||||||
|
LOG_VERBOSE(("%p: Seeking to %" PRIu64, selfHolder->mStream.get(),
|
||||||
|
offset.value()));
|
||||||
|
|
||||||
nsCOMPtr<nsIInputStream> inputStream;
|
QM_TRY(MOZ_TO_RESULT(selfHolder->mStream->Seek(
|
||||||
nsCOMPtr<nsIOutputStream> outputStream;
|
nsISeekableStream::NS_SEEK_SET, offset.value())),
|
||||||
|
CreateAndRejectBoolPromise);
|
||||||
|
}
|
||||||
|
|
||||||
if (aRead) {
|
nsCOMPtr<nsIInputStream> inputStream;
|
||||||
LOG_VERBOSE(("%p: Reading %zu bytes", selfHolder->mStream.get(),
|
nsCOMPtr<nsIOutputStream> outputStream;
|
||||||
dataSpan.Length()));
|
|
||||||
|
|
||||||
inputStream = selfHolder->mStream->InputStream();
|
if (aRead) {
|
||||||
|
LOG_VERBOSE(("%p: Reading %zu bytes", selfHolder->mStream.get(),
|
||||||
|
aData.Length()));
|
||||||
|
|
||||||
outputStream =
|
inputStream = selfHolder->mStream->InputStream();
|
||||||
FixedBufferOutputStream::Create(AsWritableChars(dataSpan));
|
outputStream =
|
||||||
} else {
|
FixedBufferOutputStream::Create(AsWritableChars(aData));
|
||||||
LOG_VERBOSE(("%p: Writing %zu bytes", selfHolder->mStream.get(),
|
} else {
|
||||||
dataSpan.Length()));
|
LOG_VERBOSE(("%p: Writing %zu bytes", selfHolder->mStream.get(),
|
||||||
|
aData.Length()));
|
||||||
|
|
||||||
QM_TRY(MOZ_TO_RESULT(NS_NewByteInputStream(
|
QM_TRY(MOZ_TO_RESULT(NS_NewByteInputStream(
|
||||||
getter_AddRefs(inputStream), AsChars(dataSpan),
|
getter_AddRefs(inputStream), AsChars(aData),
|
||||||
NS_ASSIGNMENT_DEPEND)),
|
NS_ASSIGNMENT_DEPEND)),
|
||||||
|
CreateAndRejectBoolPromise);
|
||||||
|
|
||||||
|
outputStream = selfHolder->mStream->OutputStream();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto promiseHolder = MakeUnique<MozPromiseHolder<BoolPromise>>();
|
||||||
|
RefPtr<BoolPromise> promise = promiseHolder->Ensure(__func__);
|
||||||
|
|
||||||
|
QM_TRY(MOZ_TO_RESULT(fs::AsyncCopy(
|
||||||
|
inputStream, outputStream, GetCurrentSerialEventTarget(),
|
||||||
|
aRead ? NS_ASYNCCOPY_VIA_WRITESEGMENTS
|
||||||
|
: NS_ASYNCCOPY_VIA_READSEGMENTS,
|
||||||
|
/* aCloseSource */ !aRead, /* aCloseSink */ aRead,
|
||||||
|
[&totalCount](uint32_t count) { totalCount += count; },
|
||||||
|
[promiseHolder = std::move(promiseHolder)](nsresult rv) {
|
||||||
|
promiseHolder->ResolveIfExists(true, __func__);
|
||||||
|
})),
|
||||||
CreateAndRejectBoolPromise);
|
CreateAndRejectBoolPromise);
|
||||||
|
|
||||||
outputStream = selfHolder->mStream->OutputStream();
|
return promise;
|
||||||
}
|
})
|
||||||
|
->Then(syncLoopTarget, __func__,
|
||||||
|
[this, &syncLoopTarget](
|
||||||
|
const BoolPromise::ResolveOrRejectValue& aValue) {
|
||||||
|
MOZ_ASSERT(mWorkerRef);
|
||||||
|
|
||||||
auto promiseHolder = MakeUnique<MozPromiseHolder<BoolPromise>>();
|
mWorkerRef->Private()->AssertIsOnWorkerThread();
|
||||||
RefPtr<BoolPromise> promise = promiseHolder->Ensure(__func__);
|
|
||||||
|
|
||||||
QM_TRY(MOZ_TO_RESULT(fs::AsyncCopy(
|
mWorkerRef->Private()->StopSyncLoop(syncLoopTarget, NS_OK);
|
||||||
inputStream, outputStream, GetCurrentSerialEventTarget(),
|
});
|
||||||
aRead ? NS_ASYNCCOPY_VIA_WRITESEGMENTS
|
|
||||||
: NS_ASYNCCOPY_VIA_READSEGMENTS,
|
|
||||||
/* aCloseSource */ !aRead, /* aCloseSink */ aRead,
|
|
||||||
[&totalCount](uint32_t count) { totalCount += count; },
|
|
||||||
[promiseHolder = std::move(promiseHolder)](nsresult rv) {
|
|
||||||
promiseHolder->ResolveIfExists(true, __func__);
|
|
||||||
})),
|
|
||||||
CreateAndRejectBoolPromise);
|
|
||||||
|
|
||||||
return promise;
|
MOZ_ALWAYS_SUCCEEDS(syncLoop.Run());
|
||||||
})
|
});
|
||||||
->Then(syncLoopTarget, __func__,
|
|
||||||
[this, &syncLoopTarget](
|
|
||||||
const BoolPromise::ResolveOrRejectValue& aValue) {
|
|
||||||
MOZ_ASSERT(mWorkerRef);
|
|
||||||
|
|
||||||
mWorkerRef->Private()->AssertIsOnWorkerThread();
|
|
||||||
|
|
||||||
mWorkerRef->Private()->StopSyncLoop(syncLoopTarget, NS_OK);
|
|
||||||
});
|
|
||||||
|
|
||||||
MOZ_ALWAYS_SUCCEEDS(syncLoop.Run());
|
|
||||||
|
|
||||||
return totalCount;
|
return totalCount;
|
||||||
}
|
}
|
||||||
|
@ -23,24 +23,6 @@ LogModule* GetEMEVerboseLog() {
|
|||||||
return log;
|
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(
|
void CopyArrayBufferViewOrArrayBufferData(
|
||||||
const dom::ArrayBufferViewOrArrayBuffer& aBufferOrView,
|
const dom::ArrayBufferViewOrArrayBuffer& aBufferOrView,
|
||||||
nsTArray<uint8_t>& aOutData) {
|
nsTArray<uint8_t>& aOutData) {
|
||||||
|
@ -47,36 +47,6 @@ void CopyArrayBufferViewOrArrayBufferData(
|
|||||||
const dom::ArrayBufferViewOrArrayBuffer& aBufferOrView,
|
const dom::ArrayBufferViewOrArrayBuffer& aBufferOrView,
|
||||||
nsTArray<uint8_t>& aOutData);
|
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);
|
nsString KeySystemToProxyName(const nsAString& aKeySystem);
|
||||||
|
|
||||||
bool IsClearkeyKeySystem(const nsAString& aKeySystem);
|
bool IsClearkeyKeySystem(const nsAString& aKeySystem);
|
||||||
|
@ -35,36 +35,36 @@ nsPIDOMWindowInner* MediaKeyStatusMap::GetParentObject() const {
|
|||||||
return mParent;
|
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,
|
void MediaKeyStatusMap::Get(const ArrayBufferViewOrArrayBuffer& aKey,
|
||||||
OwningMediaKeyStatusOrUndefined& aOutValue,
|
OwningMediaKeyStatusOrUndefined& aOutValue,
|
||||||
ErrorResult& aOutRv) const {
|
ErrorResult& aOutRv) const {
|
||||||
ArrayData keyId = GetArrayBufferViewOrArrayBufferData(aKey);
|
const KeyStatus* status = FindKey(aKey);
|
||||||
if (!keyId.IsValid()) {
|
if (!status) {
|
||||||
aOutValue.SetUndefined();
|
aOutValue.SetUndefined();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (const KeyStatus& status : mStatuses) {
|
|
||||||
if (keyId == status.mKeyId) {
|
aOutValue.SetAsMediaKeyStatus() = status->mStatus;
|
||||||
aOutValue.SetAsMediaKeyStatus() = status.mStatus;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
aOutValue.SetUndefined();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MediaKeyStatusMap::Has(const ArrayBufferViewOrArrayBuffer& aKey) const {
|
bool MediaKeyStatusMap::Has(const ArrayBufferViewOrArrayBuffer& aKey) const {
|
||||||
ArrayData keyId = GetArrayBufferViewOrArrayBufferData(aKey);
|
return FindKey(aKey);
|
||||||
if (!keyId.IsValid()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const KeyStatus& status : mStatuses) {
|
|
||||||
if (keyId == status.mKeyId) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t MediaKeyStatusMap::GetIterableLength() const {
|
uint32_t MediaKeyStatusMap::GetIterableLength() const {
|
||||||
|
@ -83,6 +83,8 @@ class MediaKeyStatusMap final : public nsISupports, public nsWrapperCache {
|
|||||||
MediaKeyStatus mStatus;
|
MediaKeyStatus mStatus;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const KeyStatus* FindKey(const ArrayBufferViewOrArrayBuffer& aKey) const;
|
||||||
|
|
||||||
nsTArray<KeyStatus> mStatuses;
|
nsTArray<KeyStatus> mStatuses;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#include "mozilla/Preferences.h"
|
#include "mozilla/Preferences.h"
|
||||||
#include "mozilla/dom/MediaSourceBinding.h"
|
#include "mozilla/dom/MediaSourceBinding.h"
|
||||||
#include "mozilla/dom/TimeRanges.h"
|
#include "mozilla/dom/TimeRanges.h"
|
||||||
|
#include "mozilla/dom/TypedArray.h"
|
||||||
#include "nsError.h"
|
#include "nsError.h"
|
||||||
#include "nsIRunnable.h"
|
#include "nsIRunnable.h"
|
||||||
#include "nsThreadUtils.h"
|
#include "nsThreadUtils.h"
|
||||||
@ -178,18 +179,24 @@ void SourceBuffer::SetAppendWindowEnd(double aAppendWindowEnd,
|
|||||||
void SourceBuffer::AppendBuffer(const ArrayBuffer& aData, ErrorResult& aRv) {
|
void SourceBuffer::AppendBuffer(const ArrayBuffer& aData, ErrorResult& aRv) {
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
MSE_API("AppendBuffer(ArrayBuffer)");
|
MSE_API("AppendBuffer(ArrayBuffer)");
|
||||||
aData.ComputeState();
|
RefPtr<MediaByteBuffer> data = PrepareAppend(aData, aRv);
|
||||||
DDLOG(DDLogCategory::API, "AppendBuffer", aData.Length());
|
if (!data) {
|
||||||
AppendData(aData.Data(), aData.Length(), aRv);
|
return;
|
||||||
|
}
|
||||||
|
DDLOG(DDLogCategory::API, "AppendBuffer", uint64_t(data->Length()));
|
||||||
|
AppendData(std::move(data), aRv);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SourceBuffer::AppendBuffer(const ArrayBufferView& aData,
|
void SourceBuffer::AppendBuffer(const ArrayBufferView& aData,
|
||||||
ErrorResult& aRv) {
|
ErrorResult& aRv) {
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
MSE_API("AppendBuffer(ArrayBufferView)");
|
MSE_API("AppendBuffer(ArrayBufferView)");
|
||||||
aData.ComputeState();
|
RefPtr<MediaByteBuffer> data = PrepareAppend(aData, aRv);
|
||||||
DDLOG(DDLogCategory::API, "AppendBuffer", aData.Length());
|
if (!data) {
|
||||||
AppendData(aData.Data(), aData.Length(), aRv);
|
return;
|
||||||
|
}
|
||||||
|
DDLOG(DDLogCategory::API, "AppendBuffer", uint64_t(data->Length()));
|
||||||
|
AppendData(std::move(data), aRv);
|
||||||
}
|
}
|
||||||
|
|
||||||
already_AddRefed<Promise> SourceBuffer::AppendBufferAsync(
|
already_AddRefed<Promise> SourceBuffer::AppendBufferAsync(
|
||||||
@ -197,10 +204,13 @@ already_AddRefed<Promise> SourceBuffer::AppendBufferAsync(
|
|||||||
MOZ_ASSERT(NS_IsMainThread());
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
|
||||||
MSE_API("AppendBufferAsync(ArrayBuffer)");
|
MSE_API("AppendBufferAsync(ArrayBuffer)");
|
||||||
aData.ComputeState();
|
RefPtr<MediaByteBuffer> data = PrepareAppend(aData, aRv);
|
||||||
DDLOG(DDLogCategory::API, "AppendBufferAsync", aData.Length());
|
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(
|
already_AddRefed<Promise> SourceBuffer::AppendBufferAsync(
|
||||||
@ -208,10 +218,13 @@ already_AddRefed<Promise> SourceBuffer::AppendBufferAsync(
|
|||||||
MOZ_ASSERT(NS_IsMainThread());
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
|
||||||
MSE_API("AppendBufferAsync(ArrayBufferView)");
|
MSE_API("AppendBufferAsync(ArrayBufferView)");
|
||||||
aData.ComputeState();
|
RefPtr<MediaByteBuffer> data = PrepareAppend(aData, aRv);
|
||||||
DDLOG(DDLogCategory::API, "AppendBufferAsync", aData.Length());
|
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) {
|
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) {
|
ErrorResult& aRv) {
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
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();
|
StartUpdating();
|
||||||
|
|
||||||
mTrackBuffersManager->AppendData(data.forget(), mCurrentAttributes)
|
mTrackBuffersManager->AppendData(aData.forget(), mCurrentAttributes)
|
||||||
->Then(mAbstractMainThread, __func__, this,
|
->Then(mAbstractMainThread, __func__, this,
|
||||||
&SourceBuffer::AppendDataCompletedWithSuccess,
|
&SourceBuffer::AppendDataCompletedWithSuccess,
|
||||||
&SourceBuffer::AppendDataErrored)
|
&SourceBuffer::AppendDataErrored)
|
||||||
->Track(mPendingAppend);
|
->Track(mPendingAppend);
|
||||||
}
|
}
|
||||||
|
|
||||||
already_AddRefed<Promise> SourceBuffer::AppendDataAsync(const uint8_t* aData,
|
already_AddRefed<Promise> SourceBuffer::AppendDataAsync(
|
||||||
uint32_t aLength,
|
RefPtr<MediaByteBuffer>&& aData, ErrorResult& aRv) {
|
||||||
ErrorResult& aRv) {
|
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
|
||||||
if (!IsAttached()) {
|
if (!IsAttached()) {
|
||||||
@ -586,7 +594,7 @@ already_AddRefed<Promise> SourceBuffer::AppendDataAsync(const uint8_t* aData,
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
AppendData(aData, aLength, aRv);
|
AppendData(std::move(aData), aRv);
|
||||||
|
|
||||||
if (aRv.Failed()) {
|
if (aRv.Failed()) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@ -718,6 +726,13 @@ already_AddRefed<MediaByteBuffer> SourceBuffer::PrepareAppend(
|
|||||||
return data.forget();
|
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() {
|
TimeUnit SourceBuffer::GetBufferedEnd() {
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
ErrorResult dummy;
|
ErrorResult dummy;
|
||||||
|
@ -151,10 +151,10 @@ class SourceBuffer final : public DOMEventTargetHelper,
|
|||||||
void CheckEndTime();
|
void CheckEndTime();
|
||||||
|
|
||||||
// Shared implementation of AppendBuffer overloads.
|
// 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.
|
// Shared implementation of AppendBufferAsync overloads.
|
||||||
already_AddRefed<Promise> AppendDataAsync(const uint8_t* aData,
|
already_AddRefed<Promise> AppendDataAsync(RefPtr<MediaByteBuffer>&& aData,
|
||||||
uint32_t aLength, ErrorResult& aRv);
|
ErrorResult& aRv);
|
||||||
|
|
||||||
void PrepareRemove(double aStart, double aEnd, 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,
|
already_AddRefed<MediaByteBuffer> PrepareAppend(const uint8_t* aData,
|
||||||
uint32_t aLength,
|
uint32_t aLength,
|
||||||
ErrorResult& aRv);
|
ErrorResult& aRv);
|
||||||
|
template <typename T>
|
||||||
|
already_AddRefed<MediaByteBuffer> PrepareAppend(const T& aData,
|
||||||
|
ErrorResult& aRv);
|
||||||
|
|
||||||
void AppendDataCompletedWithSuccess(
|
void AppendDataCompletedWithSuccess(
|
||||||
const SourceBufferTask::AppendBufferResult& aResult);
|
const SourceBufferTask::AppendBufferResult& aResult);
|
||||||
|
@ -215,15 +215,14 @@ void AnalyserNode::GetFloatFrequencyData(const Float32Array& aArray) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
aArray.ComputeState();
|
aArray.ProcessData([&](const Span<float>& aData, JS::AutoCheckCannotGC&&) {
|
||||||
|
size_t length = std::min(size_t(aData.Length()), mOutputBuffer.Length());
|
||||||
|
|
||||||
float* buffer = aArray.Data();
|
for (size_t i = 0; i < length; ++i) {
|
||||||
size_t length = std::min(size_t(aArray.Length()), mOutputBuffer.Length());
|
aData[i] = WebAudioUtils::ConvertLinearToDecibels(
|
||||||
|
mOutputBuffer[i], -std::numeric_limits<float>::infinity());
|
||||||
for (size_t i = 0; i < length; ++i) {
|
}
|
||||||
buffer[i] = WebAudioUtils::ConvertLinearToDecibels(
|
});
|
||||||
mOutputBuffer[i], -std::numeric_limits<float>::infinity());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnalyserNode::GetByteFrequencyData(const Uint8Array& aArray) {
|
void AnalyserNode::GetByteFrequencyData(const Uint8Array& aArray) {
|
||||||
@ -234,51 +233,50 @@ void AnalyserNode::GetByteFrequencyData(const Uint8Array& aArray) {
|
|||||||
|
|
||||||
const double rangeScaleFactor = 1.0 / (mMaxDecibels - mMinDecibels);
|
const double rangeScaleFactor = 1.0 / (mMaxDecibels - mMinDecibels);
|
||||||
|
|
||||||
aArray.ComputeState();
|
aArray.ProcessData([&](const Span<uint8_t>& aData, JS::AutoCheckCannotGC&&) {
|
||||||
|
size_t length = std::min(size_t(aData.Length()), mOutputBuffer.Length());
|
||||||
|
|
||||||
unsigned char* buffer = aArray.Data();
|
for (size_t i = 0; i < length; ++i) {
|
||||||
size_t length = std::min(size_t(aArray.Length()), mOutputBuffer.Length());
|
const double decibels = WebAudioUtils::ConvertLinearToDecibels(
|
||||||
|
mOutputBuffer[i], mMinDecibels);
|
||||||
for (size_t i = 0; i < length; ++i) {
|
// scale down the value to the range of [0, UCHAR_MAX]
|
||||||
const double decibels =
|
const double scaled = std::max(
|
||||||
WebAudioUtils::ConvertLinearToDecibels(mOutputBuffer[i], mMinDecibels);
|
0.0,
|
||||||
// scale down the value to the range of [0, UCHAR_MAX]
|
std::min(double(UCHAR_MAX),
|
||||||
const double scaled = std::max(
|
UCHAR_MAX * (decibels - mMinDecibels) * rangeScaleFactor));
|
||||||
0.0, std::min(double(UCHAR_MAX), UCHAR_MAX * (decibels - mMinDecibels) *
|
aData[i] = static_cast<unsigned char>(scaled);
|
||||||
rangeScaleFactor));
|
}
|
||||||
buffer[i] = static_cast<unsigned char>(scaled);
|
});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnalyserNode::GetFloatTimeDomainData(const Float32Array& aArray) {
|
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();
|
GetTimeDomainData(aData.Elements(), length);
|
||||||
size_t length = std::min(aArray.Length(), FftSize());
|
});
|
||||||
|
|
||||||
GetTimeDomainData(buffer, length);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnalyserNode::GetByteTimeDomainData(const Uint8Array& aArray) {
|
void AnalyserNode::GetByteTimeDomainData(const Uint8Array& aArray) {
|
||||||
aArray.ComputeState();
|
aArray.ProcessData([&](const Span<uint8_t>& aData, JS::AutoCheckCannotGC&&) {
|
||||||
|
size_t length = std::min(aData.Length(), size_t(FftSize()));
|
||||||
|
|
||||||
size_t length = std::min(aArray.Length(), FftSize());
|
AlignedTArray<float> tmpBuffer;
|
||||||
|
if (!tmpBuffer.SetLength(length, fallible)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
AlignedTArray<float> tmpBuffer;
|
GetTimeDomainData(tmpBuffer.Elements(), length);
|
||||||
if (!tmpBuffer.SetLength(length, fallible)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
GetTimeDomainData(tmpBuffer.Elements(), length);
|
unsigned char* buffer = aData.Elements();
|
||||||
|
for (size_t i = 0; i < length; ++i) {
|
||||||
unsigned char* buffer = aArray.Data();
|
const float value = tmpBuffer[i];
|
||||||
for (size_t i = 0; i < length; ++i) {
|
// scale the value to the range of [0, UCHAR_MAX]
|
||||||
const float value = tmpBuffer[i];
|
const float scaled =
|
||||||
// scale the value to the range of [0, UCHAR_MAX]
|
std::max(0.0f, std::min(float(UCHAR_MAX), 128.0f * (value + 1.0f)));
|
||||||
const float scaled =
|
buffer[i] = static_cast<unsigned char>(scaled);
|
||||||
std::max(0.0f, std::min(float(UCHAR_MAX), 128.0f * (value + 1.0f)));
|
}
|
||||||
buffer[i] = static_cast<unsigned char>(scaled);
|
});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AnalyserNode::FFTAnalysis() {
|
bool AnalyserNode::FFTAnalysis() {
|
||||||
|
@ -323,8 +323,10 @@ void AudioBuffer::CopyFromChannel(const Float32Array& aDestination,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
JS::AutoCheckCannotGC nogc;
|
JS::AutoCheckCannotGC nogc;
|
||||||
aDestination.ComputeState();
|
MOZ_RELEASE_ASSERT(!JS_GetTypedArraySharedness(aDestination.Obj()));
|
||||||
uint32_t count = std::min(length - aBufferOffset, aDestination.Length());
|
auto calculateCount = [=](uint32_t aLength) -> uint32_t {
|
||||||
|
return std::min(length - aBufferOffset, aLength);
|
||||||
|
};
|
||||||
|
|
||||||
JSObject* channelArray = mJSChannels[aChannelNumber];
|
JSObject* channelArray = mJSChannels[aChannelNumber];
|
||||||
if (channelArray) {
|
if (channelArray) {
|
||||||
@ -338,17 +340,27 @@ void AudioBuffer::CopyFromChannel(const Float32Array& aDestination,
|
|||||||
// The sourceData arrays should all have originated in
|
// The sourceData arrays should all have originated in
|
||||||
// RestoreJSChannelData, where they are created unshared.
|
// RestoreJSChannelData, where they are created unshared.
|
||||||
MOZ_ASSERT(!isShared);
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!mSharedChannels.IsNull()) {
|
if (!mSharedChannels.IsNull()) {
|
||||||
CopyChannelDataToFloat(mSharedChannels, aChannelNumber, aBufferOffset,
|
aDestination.ProcessData([&](const Span<float>& aData,
|
||||||
aDestination.Data(), count);
|
JS::AutoCheckCannotGC&&) {
|
||||||
|
CopyChannelDataToFloat(mSharedChannels, aChannelNumber, aBufferOffset,
|
||||||
|
aData.Elements(), calculateCount(aData.Length()));
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
PodZero(aDestination.Data(), count);
|
aDestination.ProcessData(
|
||||||
|
[&](const Span<float>& aData, JS::AutoCheckCannotGC&&) {
|
||||||
|
PodZero(aData.Elements(), calculateCount(aData.Length()));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioBuffer::CopyToChannel(JSContext* aJSContext,
|
void AudioBuffer::CopyToChannel(JSContext* aJSContext,
|
||||||
@ -374,14 +386,20 @@ void AudioBuffer::CopyToChannel(JSContext* aJSContext,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
aSource.ComputeState();
|
int64_t offset = aBufferOffset;
|
||||||
uint32_t count = std::min(length - aBufferOffset, aSource.Length());
|
aSource.ProcessData([&](const Span<float>& aData, JS::AutoCheckCannotGC&&) {
|
||||||
bool isShared = false;
|
MOZ_ASSERT_IF(std::numeric_limits<decltype(aData.Length())>::max() >
|
||||||
float* channelData = JS_GetFloat32ArrayData(channelArray, &isShared, nogc);
|
std::numeric_limits<int64_t>::max(),
|
||||||
// The channelData arrays should all have originated in
|
aData.Length() <= std::numeric_limits<int64_t>::max());
|
||||||
// RestoreJSChannelData, where they are created unshared.
|
int64_t srcLength = int64_t(aData.Length());
|
||||||
MOZ_ASSERT(!isShared);
|
size_t count = std::max(int64_t(0), std::min(length - offset, srcLength));
|
||||||
PodMove(channelData + aBufferOffset, aSource.Data(), count);
|
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, aData.Elements(), count);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioBuffer::GetChannelData(JSContext* aJSContext, uint32_t aChannel,
|
void AudioBuffer::GetChannelData(JSContext* aJSContext, uint32_t aChannel,
|
||||||
|
@ -659,19 +659,18 @@ already_AddRefed<Promise> AudioContext::DecodeAudioData(
|
|||||||
}
|
}
|
||||||
|
|
||||||
JSAutoRealm ar(cx, obj);
|
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
|
// Throw if the buffer is detached
|
||||||
aRv.ThrowTypeError("Buffer argument can't be a detached buffer");
|
aRv.ThrowTypeError("Buffer argument can't be a detached buffer");
|
||||||
return nullptr;
|
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.
|
// Sniff the content of the media.
|
||||||
// Failed type sniffing will be handled by AsyncDecodeWebAudio.
|
// Failed type sniffing will be handled by AsyncDecodeWebAudio.
|
||||||
nsAutoCString contentType;
|
nsAutoCString contentType;
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
#include "BiquadFilterNode.h"
|
#include "BiquadFilterNode.h"
|
||||||
|
#include <algorithm>
|
||||||
#include "AlignmentUtils.h"
|
#include "AlignmentUtils.h"
|
||||||
#include "AudioNodeEngine.h"
|
#include "AudioNodeEngine.h"
|
||||||
#include "AudioNodeTrack.h"
|
#include "AudioNodeTrack.h"
|
||||||
@ -304,46 +305,54 @@ void BiquadFilterNode::GetFrequencyResponse(const Float32Array& aFrequencyHz,
|
|||||||
const Float32Array& aMagResponse,
|
const Float32Array& aMagResponse,
|
||||||
const Float32Array& aPhaseResponse,
|
const Float32Array& aPhaseResponse,
|
||||||
ErrorResult& aRv) {
|
ErrorResult& aRv) {
|
||||||
aFrequencyHz.ComputeState();
|
UniquePtr<float[]> frequencies;
|
||||||
aMagResponse.ComputeState();
|
size_t length;
|
||||||
aPhaseResponse.ComputeState();
|
|
||||||
|
|
||||||
if (!(aFrequencyHz.Length() == aMagResponse.Length() &&
|
|
||||||
aMagResponse.Length() == aPhaseResponse.Length())) {
|
|
||||||
aRv.ThrowInvalidAccessError("Parameter lengths must match");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t length = aFrequencyHz.Length();
|
|
||||||
if (!length) {
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const double currentTime = Context()->CurrentTime();
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
double freq = mFrequency->GetValueAtTime(currentTime);
|
if (length == 0) {
|
||||||
double q = mQ->GetValueAtTime(currentTime);
|
return;
|
||||||
double gain = mGain->GetValueAtTime(currentTime);
|
}
|
||||||
double detune = mDetune->GetValueAtTime(currentTime);
|
|
||||||
|
|
||||||
WebCore::Biquad biquad;
|
frequencies = MakeUniqueForOverwriteFallible<float[]>(length);
|
||||||
SetParamsOnBiquad(biquad, Context()->SampleRate(), mType, freq, q, gain,
|
if (!frequencies) {
|
||||||
detune);
|
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||||
biquad.getFrequencyResponse(int(length), frequencies.get(),
|
return;
|
||||||
aMagResponse.Data(), aPhaseResponse.Data());
|
}
|
||||||
|
|
||||||
|
const double nyquist = Context()->SampleRate() * 0.5;
|
||||||
|
std::transform(aFrequencyData.begin(), aFrequencyData.end(),
|
||||||
|
frequencies.get(), [&](float aFrequency) {
|
||||||
|
if (aFrequency >= 0 && aFrequency <= nyquist) {
|
||||||
|
return static_cast<float>(aFrequency / nyquist);
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::numeric_limits<float>::quiet_NaN();
|
||||||
|
});
|
||||||
|
|
||||||
|
double freq = mFrequency->GetValueAtTime(currentTime);
|
||||||
|
double q = mQ->GetValueAtTime(currentTime);
|
||||||
|
double gain = mGain->GetValueAtTime(currentTime);
|
||||||
|
double detune = mDetune->GetValueAtTime(currentTime);
|
||||||
|
|
||||||
|
WebCore::Biquad biquad;
|
||||||
|
SetParamsOnBiquad(biquad, Context()->SampleRate(), mType, freq, q, gain,
|
||||||
|
detune);
|
||||||
|
biquad.getFrequencyResponse(int(length), frequencies.get(),
|
||||||
|
aMagData.Elements(), aPhaseData.Elements());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace mozilla::dom
|
} // namespace mozilla::dom
|
||||||
|
@ -226,33 +226,41 @@ JSObject* IIRFilterNode::WrapObject(JSContext* aCx,
|
|||||||
void IIRFilterNode::GetFrequencyResponse(const Float32Array& aFrequencyHz,
|
void IIRFilterNode::GetFrequencyResponse(const Float32Array& aFrequencyHz,
|
||||||
const Float32Array& aMagResponse,
|
const Float32Array& aMagResponse,
|
||||||
const Float32Array& aPhaseResponse) {
|
const Float32Array& aPhaseResponse) {
|
||||||
aFrequencyHz.ComputeState();
|
aFrequencyHz.ProcessData([&](const Span<float>& aFrequencyData,
|
||||||
aMagResponse.ComputeState();
|
JS::AutoCheckCannotGC&&) {
|
||||||
aPhaseResponse.ComputeState();
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t length =
|
auto frequencies = MakeUniqueForOverwriteFallible<float[]>(length);
|
||||||
std::min(std::min(aFrequencyHz.Length(), aMagResponse.Length()),
|
if (!frequencies) {
|
||||||
aPhaseResponse.Length());
|
return;
|
||||||
if (!length) {
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto frequencies = MakeUnique<float[]>(length);
|
const double nyquist = Context()->SampleRate() * 0.5;
|
||||||
float* frequencyHz = aFrequencyHz.Data();
|
|
||||||
const double nyquist = Context()->SampleRate() * 0.5;
|
|
||||||
|
|
||||||
// Normalize the frequencies
|
// Normalize the frequencies
|
||||||
for (uint32_t i = 0; i < length; ++i) {
|
std::transform(aFrequencyData.begin(), aFrequencyData.begin() + length,
|
||||||
if (frequencyHz[i] >= 0 && frequencyHz[i] <= nyquist) {
|
frequencies.get(), [&](float aFrequency) {
|
||||||
frequencies[i] = static_cast<float>(frequencyHz[i] / nyquist);
|
if (aFrequency >= 0 && aFrequency <= nyquist) {
|
||||||
} else {
|
return static_cast<float>(aFrequency / nyquist);
|
||||||
frequencies[i] = std::numeric_limits<float>::quiet_NaN();
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
blink::IIRFilter filter(&mFeedforward, &mFeedback);
|
return std::numeric_limits<float>::quiet_NaN();
|
||||||
filter.getFrequencyResponse(int(length), frequencies.get(),
|
});
|
||||||
aMagResponse.Data(), aPhaseResponse.Data());
|
|
||||||
|
blink::IIRFilter filter(&mFeedforward, &mFeedback);
|
||||||
|
filter.getFrequencyResponse(int(length), frequencies.get(),
|
||||||
|
aMagData.Elements(), aPhaseData.Elements());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace mozilla::dom
|
} // namespace mozilla::dom
|
||||||
|
@ -144,32 +144,30 @@ already_AddRefed<EncodedVideoChunk> EncodedVideoChunk::Constructor(
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto r = GetSharedArrayBufferData(aInit.mData);
|
auto buffer = ProcessTypedArrays(
|
||||||
if (r.isErr()) {
|
aInit.mData,
|
||||||
aRv.Throw(r.unwrapErr());
|
[&](const Span<uint8_t>& aData,
|
||||||
return nullptr;
|
JS::AutoCheckCannotGC&&) -> RefPtr<MediaAlignedByteBuffer> {
|
||||||
}
|
// Make sure it's in uint32_t's range.
|
||||||
Span<uint8_t> buf = r.unwrap();
|
CheckedUint32 byteLength(aData.Length());
|
||||||
|
if (!byteLength.isValid()) {
|
||||||
|
aRv.Throw(NS_ERROR_INVALID_ARG);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
if (aData.Length() == 0) {
|
||||||
|
LOGW("Buffer for constructing EncodedVideoChunk is empty!");
|
||||||
|
}
|
||||||
|
RefPtr<MediaAlignedByteBuffer> buf = MakeRefPtr<MediaAlignedByteBuffer>(
|
||||||
|
aData.Elements(), aData.Length());
|
||||||
|
|
||||||
// Make sure it's in uint32_t's range.
|
// Instead of checking *buf, size comparision is used to allow
|
||||||
CheckedUint32 byteLength(buf.size_bytes());
|
// constructing a zero-sized EncodedVideoChunk.
|
||||||
if (!byteLength.isValid()) {
|
if (!buf || buf->Size() != aData.Length()) {
|
||||||
aRv.Throw(NS_ERROR_INVALID_ARG);
|
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
return buf;
|
||||||
if (buf.size_bytes() == 0) {
|
});
|
||||||
LOGW("Buffer for constructing EncodedVideoChunk is empty!");
|
|
||||||
}
|
|
||||||
|
|
||||||
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()) {
|
|
||||||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
RefPtr<EncodedVideoChunk> chunk(new EncodedVideoChunk(
|
RefPtr<EncodedVideoChunk> chunk(new EncodedVideoChunk(
|
||||||
global, buffer.forget(), aInit.mType, aInit.mTimestamp,
|
global, buffer.forget(), aInit.mType, aInit.mTimestamp,
|
||||||
@ -207,20 +205,15 @@ void EncodedVideoChunk::CopyTo(
|
|||||||
ErrorResult& aRv) {
|
ErrorResult& aRv) {
|
||||||
AssertIsOnOwningThread();
|
AssertIsOnOwningThread();
|
||||||
|
|
||||||
auto r = GetSharedArrayBufferData(aDestination);
|
ProcessTypedArraysFixed(aDestination, [&](const Span<uint8_t>& aData) {
|
||||||
if (r.isErr()) {
|
if (mBuffer->Size() > aData.size_bytes()) {
|
||||||
aRv.Throw(r.unwrapErr());
|
aRv.ThrowTypeError(
|
||||||
return;
|
"Destination ArrayBuffer smaller than source EncodedVideoChunk");
|
||||||
}
|
return;
|
||||||
Span<uint8_t> buf = r.unwrap();
|
}
|
||||||
|
|
||||||
if (mBuffer->Size() > buf.size_bytes()) {
|
PodCopy(aData.data(), mBuffer->Data(), mBuffer->Size());
|
||||||
aRv.ThrowTypeError(
|
});
|
||||||
"Destination ArrayBuffer smaller than source EncodedVideoChunk");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
PodCopy(buf.data(), mBuffer->Data(), mBuffer->Size());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://w3c.github.io/webcodecs/#ref-for-deserialization-steps%E2%91%A0
|
// 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,
|
MOZ_TRY_VAR(combinedLayout,
|
||||||
ComputeLayoutAndAllocationSize(parsedRect, format, optLayout));
|
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);
|
Maybe<uint64_t> duration = OptionalToMaybe(aInit.mDuration);
|
||||||
|
|
||||||
VideoColorSpaceInit colorSpace =
|
VideoColorSpaceInit colorSpace =
|
||||||
PickColorSpace(OptionalToPointer(aInit.mColorSpace), aInit.mFormat);
|
PickColorSpace(OptionalToPointer(aInit.mColorSpace), aInit.mFormat);
|
||||||
|
|
||||||
RefPtr<layers::Image> data;
|
RefPtr<layers::Image> data;
|
||||||
MOZ_TRY_VAR(data,
|
MOZ_TRY_VAR(
|
||||||
CreateImageFromBuffer(format, colorSpace, codedSize, buffer));
|
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);
|
||||||
MOZ_ASSERT(data->GetSize() == codedSize);
|
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:
|
// TODO: Spec should assign aInit.mFormat to inner format value:
|
||||||
// https://github.com/w3c/webcodecs/issues/509.
|
// https://github.com/w3c/webcodecs/issues/509.
|
||||||
// This comment should be removed once the issue is resolved.
|
// This comment should be removed once the issue is resolved.
|
||||||
@ -1660,61 +1661,56 @@ already_AddRefed<Promise> VideoFrame::CopyTo(
|
|||||||
}
|
}
|
||||||
layout = r1.unwrap();
|
layout = r1.unwrap();
|
||||||
|
|
||||||
auto r2 = GetSharedArrayBufferData(aDestination);
|
return ProcessTypedArraysFixed(aDestination, [&](const Span<uint8_t>& aData) {
|
||||||
if (r2.isErr()) {
|
if (aData.size_bytes() < layout.mAllocationSize) {
|
||||||
p->MaybeRejectWithTypeError("Failed to get buffer");
|
p->MaybeRejectWithTypeError("Destination buffer is too small");
|
||||||
return p.forget();
|
|
||||||
}
|
|
||||||
Span<uint8_t> buffer = r2.unwrap();
|
|
||||||
|
|
||||||
if (buffer.size_bytes() < layout.mAllocationSize) {
|
|
||||||
p->MaybeRejectWithTypeError("Destination buffer is too small");
|
|
||||||
return p.forget();
|
|
||||||
}
|
|
||||||
|
|
||||||
Sequence<PlaneLayout> planeLayouts;
|
|
||||||
|
|
||||||
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.
|
|
||||||
for (size_t i = 0; i < layout.mComputedLayouts.Length(); ++i) {
|
|
||||||
ComputedPlaneLayout& l = layout.mComputedLayouts[i];
|
|
||||||
uint32_t destinationOffset = l.mDestinationOffset;
|
|
||||||
|
|
||||||
PlaneLayout* pl = planeLayouts.AppendElement(fallible);
|
|
||||||
if (!pl) {
|
|
||||||
p->MaybeRejectWithTypeError("Out of memory");
|
|
||||||
return p.forget();
|
return p.forget();
|
||||||
}
|
}
|
||||||
pl->mOffset = l.mDestinationOffset;
|
|
||||||
pl->mStride = l.mDestinationStride;
|
|
||||||
|
|
||||||
// Copy pixels of `size` starting from `origin` on planes[i] to
|
Sequence<PlaneLayout> planeLayouts;
|
||||||
// `aDestination`.
|
|
||||||
gfx::IntPoint origin(
|
nsTArray<Format::Plane> planes = mResource->mFormat->Planes();
|
||||||
l.mSourceLeftBytes / mResource->mFormat->SampleBytes(planes[i]),
|
MOZ_ASSERT(layout.mComputedLayouts.Length() == planes.Length());
|
||||||
l.mSourceTop);
|
|
||||||
gfx::IntSize size(
|
// TODO: These jobs can be run in a thread pool (bug 1780656) to unblock
|
||||||
l.mSourceWidthBytes / mResource->mFormat->SampleBytes(planes[i]),
|
// the current thread.
|
||||||
l.mSourceHeight);
|
for (size_t i = 0; i < layout.mComputedLayouts.Length(); ++i) {
|
||||||
if (!mResource->CopyTo(planes[i], {origin, size},
|
ComputedPlaneLayout& l = layout.mComputedLayouts[i];
|
||||||
buffer.From(destinationOffset),
|
uint32_t destinationOffset = l.mDestinationOffset;
|
||||||
static_cast<size_t>(l.mDestinationStride))) {
|
|
||||||
p->MaybeRejectWithTypeError(
|
PlaneLayout* pl = planeLayouts.AppendElement(fallible);
|
||||||
nsPrintfCString("Failed to copy image data in %s plane",
|
if (!pl) {
|
||||||
mResource->mFormat->PlaneName(planes[i])));
|
p->MaybeRejectWithTypeError("Out of memory");
|
||||||
return p.forget();
|
return p.forget();
|
||||||
|
}
|
||||||
|
pl->mOffset = l.mDestinationOffset;
|
||||||
|
pl->mStride = l.mDestinationStride;
|
||||||
|
|
||||||
|
// Copy pixels of `size` starting from `origin` on planes[i] to
|
||||||
|
// `aDestination`.
|
||||||
|
gfx::IntPoint origin(
|
||||||
|
l.mSourceLeftBytes / mResource->mFormat->SampleBytes(planes[i]),
|
||||||
|
l.mSourceTop);
|
||||||
|
gfx::IntSize size(
|
||||||
|
l.mSourceWidthBytes / mResource->mFormat->SampleBytes(planes[i]),
|
||||||
|
l.mSourceHeight);
|
||||||
|
if (!mResource->CopyTo(planes[i], {origin, size},
|
||||||
|
aData.From(destinationOffset),
|
||||||
|
static_cast<size_t>(l.mDestinationStride))) {
|
||||||
|
p->MaybeRejectWithTypeError(
|
||||||
|
nsPrintfCString("Failed to copy image data in %s plane",
|
||||||
|
mResource->mFormat->PlaneName(planes[i])));
|
||||||
|
return p.forget();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
MOZ_ASSERT(layout.mComputedLayouts.Length() == planes.Length());
|
MOZ_ASSERT(layout.mComputedLayouts.Length() == planes.Length());
|
||||||
// TODO: Spec doesn't resolve with a value. See
|
// TODO: Spec doesn't resolve with a value. See
|
||||||
// https://github.com/w3c/webcodecs/issues/510 This comment should be removed
|
// https://github.com/w3c/webcodecs/issues/510 This comment should be
|
||||||
// once the issue is resolved.
|
// removed once the issue is resolved.
|
||||||
p->MaybeResolve(planeLayouts);
|
p->MaybeResolve(planeLayouts);
|
||||||
return p.forget();
|
return p.forget();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://w3c.github.io/webcodecs/#dom-videoframe-clone
|
// 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.
|
* 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(
|
static std::tuple<JS::ArrayBufferOrView, size_t, size_t> GetArrayBufferInfo(
|
||||||
JSContext* aCx,
|
JSContext* aCx,
|
||||||
const OwningMaybeSharedArrayBufferViewOrMaybeSharedArrayBuffer& aBuffer) {
|
const OwningMaybeSharedArrayBufferViewOrMaybeSharedArrayBuffer& aBuffer) {
|
||||||
|
@ -73,12 +73,6 @@ Nullable<T> MaybeToNullable(const Maybe<T>& aOptional) {
|
|||||||
* Below are helpers to operate ArrayBuffer or ArrayBufferView.
|
* 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(
|
Result<Ok, nsresult> CloneBuffer(
|
||||||
JSContext* aCx,
|
JSContext* aCx,
|
||||||
OwningMaybeSharedArrayBufferViewOrMaybeSharedArrayBuffer& aDest,
|
OwningMaybeSharedArrayBufferViewOrMaybeSharedArrayBuffer& aDest,
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
#include "jsapi/RTCEncodedFrameBase.h"
|
#include "jsapi/RTCEncodedFrameBase.h"
|
||||||
|
|
||||||
|
#include "js/GCAPI.h"
|
||||||
#include "nsIGlobalObject.h"
|
#include "nsIGlobalObject.h"
|
||||||
#include "mozilla/dom/ScriptSettings.h"
|
#include "mozilla/dom/ScriptSettings.h"
|
||||||
#include "js/ArrayBuffer.h"
|
#include "js/ArrayBuffer.h"
|
||||||
@ -45,9 +46,10 @@ unsigned long RTCEncodedFrameBase::Timestamp() const { return mTimestamp; }
|
|||||||
void RTCEncodedFrameBase::SetData(const ArrayBuffer& aData) {
|
void RTCEncodedFrameBase::SetData(const ArrayBuffer& aData) {
|
||||||
mData.set(aData.Obj());
|
mData.set(aData.Obj());
|
||||||
if (mFrame) {
|
if (mFrame) {
|
||||||
aData.ComputeState();
|
aData.ProcessData([&](const Span<uint8_t>& aData, JS::AutoCheckCannotGC&&) {
|
||||||
mFrame->SetData(rtc::ArrayView<const uint8_t>(
|
mFrame->SetData(
|
||||||
static_cast<const uint8_t*>(aData.Data()), aData.Length()));
|
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
|
* 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/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
#include "js/Realm.h"
|
||||||
#include "mozilla/dom/EventBinding.h"
|
#include "mozilla/dom/EventBinding.h"
|
||||||
#include "mozilla/dom/MIDIMessageEvent.h"
|
#include "mozilla/dom/MIDIMessageEvent.h"
|
||||||
#include "mozilla/dom/MIDIMessageEventBinding.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
|
// Set data for event. Timestamp will always be set to Now() (default for
|
||||||
// event) using this constructor.
|
// event) using this constructor.
|
||||||
if (aEventInitDict.mData.WasPassed()) {
|
if (aEventInitDict.mData.WasPassed()) {
|
||||||
const auto& a = aEventInitDict.mData.Value();
|
JSAutoRealm ar(aGlobal.Context(), aGlobal.Get());
|
||||||
a.ComputeState();
|
JS::Rooted<JSObject*> data(aGlobal.Context(),
|
||||||
e->mData =
|
aEventInitDict.mData.Value().Obj());
|
||||||
Uint8Array::Create(aGlobal.Context(), owner, a.Length(), a.Data());
|
e->mData = JS_NewUint8ArrayFromArray(aGlobal.Context(), data);
|
||||||
if (NS_WARN_IF(!e->mData)) {
|
if (NS_WARN_IF(!e->mData)) {
|
||||||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -198,50 +198,44 @@ already_AddRefed<Promise> WritableStreamToOutput::WriteCallback(
|
|||||||
return nullptr;
|
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
|
// 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
|
// or the write didn't write it all. This avoids allocations and copies
|
||||||
// in common cases.
|
// in common cases.
|
||||||
MOZ_ASSERT(!mPromise);
|
MOZ_ASSERT(!mPromise);
|
||||||
MOZ_ASSERT(mWritten == 0);
|
MOZ_ASSERT(mWritten == 0);
|
||||||
uint32_t written = 0;
|
uint32_t written = 0;
|
||||||
nsresult rv = mOutput->Write(mozilla::AsChars(dataSpan).Elements(),
|
ProcessTypedArraysFixed(data, [&](const Span<uint8_t>& aData) {
|
||||||
dataSpan.Length(), &written);
|
Span<uint8_t> dataSpan = aData;
|
||||||
if (NS_FAILED(rv) && rv != NS_BASE_STREAM_WOULD_BLOCK) {
|
nsresult rv = mOutput->Write(mozilla::AsChars(dataSpan).Elements(),
|
||||||
promise->MaybeRejectWithAbortError("error writing data");
|
dataSpan.Length(), &written);
|
||||||
return promise.forget();
|
if (NS_FAILED(rv) && rv != NS_BASE_STREAM_WOULD_BLOCK) {
|
||||||
}
|
promise->MaybeRejectWithAbortError("error writing data");
|
||||||
if (NS_SUCCEEDED(rv)) {
|
return;
|
||||||
if (written == dataSpan.Length()) {
|
|
||||||
promise->MaybeResolveWithUndefined();
|
|
||||||
return promise.forget();
|
|
||||||
}
|
}
|
||||||
dataSpan = dataSpan.From(written);
|
if (NS_SUCCEEDED(rv)) {
|
||||||
|
if (written == dataSpan.Length()) {
|
||||||
|
promise->MaybeResolveWithUndefined();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dataSpan = dataSpan.From(written);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto buffer = Buffer<uint8_t>::CopyFrom(dataSpan);
|
||||||
|
if (buffer.isNothing()) {
|
||||||
|
promise->MaybeReject(NS_ERROR_OUT_OF_MEMORY);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mData = std::move(buffer);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (promise->State() != Promise::PromiseState::Pending) {
|
||||||
|
return promise.forget();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto buffer = Buffer<uint8_t>::CopyFrom(dataSpan);
|
|
||||||
if (buffer.isNothing()) {
|
|
||||||
promise->MaybeReject(NS_ERROR_OUT_OF_MEMORY);
|
|
||||||
return promise.forget();
|
|
||||||
}
|
|
||||||
mData = std::move(buffer);
|
|
||||||
mPromise = promise;
|
mPromise = promise;
|
||||||
|
|
||||||
nsCOMPtr<nsIEventTarget> target = mozilla::GetCurrentSerialEventTarget();
|
nsCOMPtr<nsIEventTarget> target = mozilla::GetCurrentSerialEventTarget();
|
||||||
rv = mOutput->AsyncWait(this, 0, 0, target);
|
nsresult rv = mOutput->AsyncWait(this, 0, 0, target);
|
||||||
if (NS_FAILED(rv)) {
|
if (NS_FAILED(rv)) {
|
||||||
ClearData();
|
ClearData();
|
||||||
promise->MaybeRejectWithUnknownError("error waiting to write data");
|
promise->MaybeRejectWithUnknownError("error waiting to write data");
|
||||||
|
@ -2775,51 +2775,51 @@ void SyncReadFile::ReadBytesInto(const Uint8Array& aDestArray,
|
|||||||
return aRv.ThrowOperationError("SyncReadFile is closed");
|
return aRv.ThrowOperationError("SyncReadFile is closed");
|
||||||
}
|
}
|
||||||
|
|
||||||
aDestArray.ComputeState();
|
aDestArray.ProcessFixedData([&](const Span<uint8_t>& aData) {
|
||||||
|
auto rangeEnd = CheckedInt64(aOffset) + aData.Length();
|
||||||
auto rangeEnd = CheckedInt64(aOffset) + aDestArray.Length();
|
if (!rangeEnd.isValid()) {
|
||||||
if (!rangeEnd.isValid()) {
|
return aRv.ThrowOperationError("Requested range overflows i64");
|
||||||
return aRv.ThrowOperationError("Requested range overflows i64");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rangeEnd.value() > mSize) {
|
|
||||||
return aRv.ThrowOperationError(
|
|
||||||
"Requested range overflows SyncReadFile size");
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t readLen{aDestArray.Length()};
|
|
||||||
if (readLen == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nsresult rv = mStream->Seek(PR_SEEK_SET, aOffset); NS_FAILED(rv)) {
|
|
||||||
return aRv.ThrowOperationError(
|
|
||||||
FormatErrorMessage(rv, "Could not seek to position %lld", aOffset));
|
|
||||||
}
|
|
||||||
|
|
||||||
Span<char> toRead(reinterpret_cast<char*>(aDestArray.Data()), readLen);
|
|
||||||
|
|
||||||
uint32_t totalRead = 0;
|
|
||||||
while (totalRead != readLen) {
|
|
||||||
// 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);
|
|
||||||
|
|
||||||
uint32_t bytesRead = 0;
|
|
||||||
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"));
|
|
||||||
}
|
}
|
||||||
if (bytesRead == 0) {
|
|
||||||
|
if (rangeEnd.value() > mSize) {
|
||||||
return aRv.ThrowOperationError(
|
return aRv.ThrowOperationError(
|
||||||
"Reading stopped before the entire array was filled");
|
"Requested range overflows SyncReadFile size");
|
||||||
}
|
}
|
||||||
totalRead += bytesRead;
|
|
||||||
toRead = toRead.From(bytesRead);
|
size_t readLen{aData.Length()};
|
||||||
}
|
if (readLen == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nsresult rv = mStream->Seek(PR_SEEK_SET, aOffset); NS_FAILED(rv)) {
|
||||||
|
return aRv.ThrowOperationError(
|
||||||
|
FormatErrorMessage(rv, "Could not seek to position %lld", aOffset));
|
||||||
|
}
|
||||||
|
|
||||||
|
Span<char> toRead = AsWritableChars(aData);
|
||||||
|
|
||||||
|
size_t totalRead = 0;
|
||||||
|
while (totalRead != readLen) {
|
||||||
|
// Read no more than INT32_MAX on each call to mStream->Read,
|
||||||
|
// otherwise it returns an error.
|
||||||
|
uint32_t bytesToReadThisChunk =
|
||||||
|
std::min(readLen - totalRead, size_t(INT32_MAX));
|
||||||
|
|
||||||
|
uint32_t bytesRead = 0;
|
||||||
|
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"));
|
||||||
|
}
|
||||||
|
if (bytesRead == 0) {
|
||||||
|
return aRv.ThrowOperationError(
|
||||||
|
"Reading stopped before the entire array was filled");
|
||||||
|
}
|
||||||
|
totalRead += bytesRead;
|
||||||
|
toRead = toRead.From(bytesRead);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void SyncReadFile::Close() { mStream = nullptr; }
|
void SyncReadFile::Close() { mStream = nullptr; }
|
||||||
|
@ -56,123 +56,49 @@ already_AddRefed<dom::Promise> Queue::OnSubmittedWorkDone(ErrorResult& aRv) {
|
|||||||
return promise.forget();
|
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,
|
void Queue::WriteBuffer(const Buffer& aBuffer, uint64_t aBufferOffset,
|
||||||
const dom::ArrayBufferViewOrArrayBuffer& aData,
|
const dom::ArrayBufferViewOrArrayBuffer& aData,
|
||||||
uint64_t aDataOffset,
|
uint64_t aDataOffset,
|
||||||
const dom::Optional<uint64_t>& aSize,
|
const dom::Optional<uint64_t>& aSize,
|
||||||
ErrorResult& aRv) {
|
ErrorResult& aRv) {
|
||||||
uint8_t* dataContents = nullptr;
|
dom::ProcessTypedArraysFixed(aData, [&](const Span<const uint8_t>& aData) {
|
||||||
uint64_t contentsSize = 0;
|
uint64_t length = aData.Length();
|
||||||
GetBufferSourceDataAndSize(aData, aDataOffset, aSize, dataContents,
|
const auto checkedSize = aSize.WasPassed()
|
||||||
contentsSize, "dataOffset", aRv);
|
? CheckedInt<size_t>(aSize.Value())
|
||||||
if (aRv.Failed()) {
|
: CheckedInt<size_t>(length) - aDataOffset;
|
||||||
return;
|
if (!checkedSize.isValid()) {
|
||||||
}
|
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (contentsSize % 4 != 0) {
|
const auto& size = checkedSize.value();
|
||||||
aRv.ThrowAbortError("Byte size must be a multiple of 4");
|
if (aDataOffset + size > length) {
|
||||||
return;
|
aRv.ThrowAbortError(nsPrintfCString("Wrong data size %" PRIuPTR, size));
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
auto alloc =
|
if (size % 4 != 0) {
|
||||||
mozilla::ipc::UnsafeSharedMemoryHandle::CreateAndMap(contentsSize);
|
aRv.ThrowAbortError("Byte size must be a multiple of 4");
|
||||||
if (alloc.isNothing()) {
|
return;
|
||||||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto handle = std::move(alloc.ref().first);
|
auto alloc = mozilla::ipc::UnsafeSharedMemoryHandle::CreateAndMap(size);
|
||||||
auto mapping = std::move(alloc.ref().second);
|
if (alloc.isNothing()) {
|
||||||
|
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
memcpy(mapping.Bytes().data(), dataContents + aDataOffset, contentsSize);
|
auto handle = std::move(alloc.ref().first);
|
||||||
ipc::ByteBuf bb;
|
auto mapping = std::move(alloc.ref().second);
|
||||||
ffi::wgpu_queue_write_buffer(aBuffer.mId, aBufferOffset, ToFFI(&bb));
|
|
||||||
if (!mBridge->SendQueueWriteAction(mId, mParent->mId, std::move(bb),
|
memcpy(mapping.Bytes().data(), aData.Elements() + aDataOffset, size);
|
||||||
std::move(handle))) {
|
ipc::ByteBuf bb;
|
||||||
MOZ_CRASH("IPC failure");
|
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,
|
void Queue::WriteTexture(const dom::GPUImageCopyTexture& aDestination,
|
||||||
@ -187,40 +113,39 @@ void Queue::WriteTexture(const dom::GPUImageCopyTexture& aDestination,
|
|||||||
ffi::WGPUExtent3d extent = {};
|
ffi::WGPUExtent3d extent = {};
|
||||||
ConvertExtent3DToFFI(aSize, &extent);
|
ConvertExtent3DToFFI(aSize, &extent);
|
||||||
|
|
||||||
uint8_t* dataContents = nullptr;
|
dom::ProcessTypedArraysFixed(aData, [&](const Span<const uint8_t>& aData) {
|
||||||
uint64_t contentsSize = 0;
|
if (aData.IsEmpty()) {
|
||||||
GetBufferSourceDataAndSize(aData, aDataLayout.mOffset,
|
aRv.ThrowAbortError("Input size cannot be zero.");
|
||||||
dom::Optional<uint64_t>(), dataContents,
|
return;
|
||||||
contentsSize, "dataLayout.offset", aRv);
|
}
|
||||||
if (aRv.Failed()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!contentsSize) {
|
const auto checkedSize =
|
||||||
aRv.ThrowAbortError("Input size cannot be zero.");
|
CheckedInt<size_t>(aData.Length()) - aDataLayout.mOffset;
|
||||||
return;
|
if (!checkedSize.isValid()) {
|
||||||
}
|
aRv.ThrowAbortError("Offset is higher than the size");
|
||||||
MOZ_ASSERT(dataContents != nullptr);
|
return;
|
||||||
|
}
|
||||||
|
const auto size = checkedSize.value();
|
||||||
|
|
||||||
auto alloc =
|
auto alloc = mozilla::ipc::UnsafeSharedMemoryHandle::CreateAndMap(size);
|
||||||
mozilla::ipc::UnsafeSharedMemoryHandle::CreateAndMap(contentsSize);
|
if (alloc.isNothing()) {
|
||||||
if (alloc.isNothing()) {
|
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
return;
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
auto handle = std::move(alloc.ref().first);
|
auto handle = std::move(alloc.ref().first);
|
||||||
auto mapping = std::move(alloc.ref().second);
|
auto mapping = std::move(alloc.ref().second);
|
||||||
|
|
||||||
memcpy(mapping.Bytes().data(), dataContents + aDataLayout.mOffset,
|
memcpy(mapping.Bytes().data(), aData.Elements() + aDataLayout.mOffset,
|
||||||
contentsSize);
|
size);
|
||||||
|
|
||||||
ipc::ByteBuf bb;
|
ipc::ByteBuf bb;
|
||||||
ffi::wgpu_queue_write_texture(copyView, dataLayout, extent, ToFFI(&bb));
|
ffi::wgpu_queue_write_texture(copyView, dataLayout, extent, ToFFI(&bb));
|
||||||
if (!mBridge->SendQueueWriteAction(mId, mParent->mId, std::move(bb),
|
if (!mBridge->SendQueueWriteAction(mId, mParent->mId, std::move(bb),
|
||||||
std::move(handle))) {
|
std::move(handle))) {
|
||||||
MOZ_CRASH("IPC failure");
|
MOZ_CRASH("IPC failure");
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static WebGLTexelFormat ToWebGLTexelFormat(gfx::SurfaceFormat aFormat) {
|
static WebGLTexelFormat ToWebGLTexelFormat(gfx::SurfaceFormat aFormat) {
|
||||||
|
@ -2389,7 +2389,7 @@ void WebSocket::Send(const ArrayBuffer& aData, ErrorResult& aRv) {
|
|||||||
AssertIsOnTargetThread();
|
AssertIsOnTargetThread();
|
||||||
|
|
||||||
static_assert(
|
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");
|
"byte-sized data required");
|
||||||
|
|
||||||
nsCString msgString;
|
nsCString msgString;
|
||||||
@ -2404,7 +2404,7 @@ void WebSocket::Send(const ArrayBufferView& aData, ErrorResult& aRv) {
|
|||||||
AssertIsOnTargetThread();
|
AssertIsOnTargetThread();
|
||||||
|
|
||||||
static_assert(
|
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");
|
"byte-sized data required");
|
||||||
|
|
||||||
nsCString msgString;
|
nsCString msgString;
|
||||||
|
@ -18,33 +18,36 @@ OwnedRustBuffer::OwnedRustBuffer(const RustBuffer& aBuf) {
|
|||||||
|
|
||||||
Result<OwnedRustBuffer, nsCString> OwnedRustBuffer::FromArrayBuffer(
|
Result<OwnedRustBuffer, nsCString> OwnedRustBuffer::FromArrayBuffer(
|
||||||
const ArrayBuffer& aArrayBuffer) {
|
const ArrayBuffer& aArrayBuffer) {
|
||||||
if (aArrayBuffer.Length() > INT32_MAX) {
|
return aArrayBuffer.ProcessData(
|
||||||
return Err("Input ArrayBuffer is too large"_ns);
|
[](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{};
|
RustCallStatus status{};
|
||||||
RustBuffer buf = uniffi_rustbuffer_alloc(
|
RustBuffer buf = uniffi_rustbuffer_alloc(
|
||||||
static_cast<int32_t>(aArrayBuffer.Length()), &status);
|
static_cast<int32_t>(aData.Length()), &status);
|
||||||
buf.len = aArrayBuffer.Length();
|
buf.len = aData.Length();
|
||||||
if (status.code != 0) {
|
if (status.code != 0) {
|
||||||
if (status.error_buf.data) {
|
if (status.error_buf.data) {
|
||||||
auto message = nsCString("uniffi_rustbuffer_alloc: ");
|
auto message = nsCString("uniffi_rustbuffer_alloc: ");
|
||||||
message.Append(
|
message.Append(nsDependentCSubstring(
|
||||||
nsDependentCSubstring(reinterpret_cast<char*>(status.error_buf.data),
|
reinterpret_cast<char*>(status.error_buf.data),
|
||||||
status.error_buf.len));
|
status.error_buf.len));
|
||||||
RustCallStatus status2{};
|
RustCallStatus status2{};
|
||||||
uniffi_rustbuffer_free(status.error_buf, &status2);
|
uniffi_rustbuffer_free(status.error_buf, &status2);
|
||||||
MOZ_RELEASE_ASSERT(status2.code == 0,
|
MOZ_RELEASE_ASSERT(status2.code == 0,
|
||||||
"Freeing a rustbuffer should never fail");
|
"Freeing a rustbuffer should never fail");
|
||||||
return Err(message);
|
return Err(message);
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
return Err("Unknown error allocating rust buffer"_ns);
|
||||||
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);
|
return OwnedRustBuffer(buf);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
OwnedRustBuffer::OwnedRustBuffer(OwnedRustBuffer&& aOther) : mBuf(aOther.mBuf) {
|
OwnedRustBuffer::OwnedRustBuffer(OwnedRustBuffer&& aOther) : mBuf(aOther.mBuf) {
|
||||||
|
@ -139,9 +139,7 @@ class ScaffoldingConverter<RustBuffer> {
|
|||||||
return Err("Bad argument type"_ns);
|
return Err("Bad argument type"_ns);
|
||||||
}
|
}
|
||||||
|
|
||||||
const dom::ArrayBuffer& arrayBuf = aValue.GetAsArrayBuffer();
|
return OwnedRustBuffer::FromArrayBuffer(aValue.GetAsArrayBuffer());
|
||||||
arrayBuf.ComputeState();
|
|
||||||
return OwnedRustBuffer::FromArrayBuffer(arrayBuf);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static RustBuffer IntoRust(OwnedRustBuffer&& aValue) {
|
static RustBuffer IntoRust(OwnedRustBuffer&& aValue) {
|
||||||
|
@ -71,18 +71,19 @@ void UniFFIPointer::Write(const ArrayBuffer& aArrayBuff, uint32_t aPosition,
|
|||||||
}
|
}
|
||||||
MOZ_LOG(sUniFFIPointerLogger, LogLevel::Info,
|
MOZ_LOG(sUniFFIPointerLogger, LogLevel::Info,
|
||||||
("[UniFFI] Writing Pointer to buffer"));
|
("[UniFFI] Writing Pointer to buffer"));
|
||||||
aArrayBuff.ComputeState();
|
|
||||||
CheckedUint32 position = aPosition;
|
aArrayBuff.ProcessData([&](const Span<uint8_t>& aData,
|
||||||
CheckedUint32 end = position + 8;
|
JS::AutoCheckCannotGC&&) {
|
||||||
if (!end.isValid() || end.value() > aArrayBuff.Length()) {
|
CheckedUint32 end = aPosition + 8;
|
||||||
aError.ThrowRangeError("position is out of range");
|
if (!end.isValid() || end.value() > aData.Length()) {
|
||||||
return;
|
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
|
// in Rust and Read(), a u64 is read as BigEndian and then converted to
|
||||||
uint8_t* data_ptr = aArrayBuff.Data() +
|
// a pointer we do the reverse here
|
||||||
aPosition; // Pointer arithmetic, move by position bytes
|
const auto& data_ptr = aData.Subspan(aPosition, 8);
|
||||||
mozilla::BigEndian::writeUint64(data_ptr, (uint64_t)GetPtr());
|
mozilla::BigEndian::writeUint64(data_ptr.Elements(), (uint64_t)GetPtr());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
UniFFIPointer::UniFFIPointer(void* aPtr, const UniFFIPointerType* aType) {
|
UniFFIPointer::UniFFIPointer(void* aPtr, const UniFFIPointerType* aType) {
|
||||||
|
Loading…
Reference in New Issue
Block a user