diff --git a/LayoutTests/http/wpt/push-api/pushEvent.any.js b/LayoutTests/http/wpt/push-api/pushEvent.any.js index 5e89f7705decd..7ecfc69f40b8e 100644 --- a/LayoutTests/http/wpt/push-api/pushEvent.any.js +++ b/LayoutTests/http/wpt/push-api/pushEvent.any.js @@ -27,8 +27,12 @@ test(() => { assert_true(data instanceof PushMessageData); assert_equals(data.text(), stringValue); assert_true(data.blob() instanceof Blob); + assert_true(data.bytes() instanceof Uint8Array); assert_equals(data.json().test, 1); assert_equals(data.arrayBuffer().byteLength, stringValue.length); + assert_equals(data.bytes().length, stringValue.length); + assert_not_equals(data.bytes().buffer, data.arrayBuffer()); + assert_not_equals(data.bytes(), data.bytes()); } }, "PushEvent with data"); diff --git a/LayoutTests/imported/w3c/web-platform-tests/FileAPI/Blob-methods-from-detached-frame-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/FileAPI/Blob-methods-from-detached-frame-expected.txt index bd72619550a29..000c75b978148 100644 --- a/LayoutTests/imported/w3c/web-platform-tests/FileAPI/Blob-methods-from-detached-frame-expected.txt +++ b/LayoutTests/imported/w3c/web-platform-tests/FileAPI/Blob-methods-from-detached-frame-expected.txt @@ -2,5 +2,6 @@ PASS slice() PASS text() PASS arrayBuffer() +PASS bytes() PASS stream() diff --git a/LayoutTests/imported/w3c/web-platform-tests/FileAPI/Blob-methods-from-detached-frame.html b/LayoutTests/imported/w3c/web-platform-tests/FileAPI/Blob-methods-from-detached-frame.html index 37efd5ed2016b..78f08e8270e2f 100644 --- a/LayoutTests/imported/w3c/web-platform-tests/FileAPI/Blob-methods-from-detached-frame.html +++ b/LayoutTests/imported/w3c/web-platform-tests/FileAPI/Blob-methods-from-detached-frame.html @@ -27,6 +27,7 @@ assert_equals(await slicedBlob.text(), "oo"); assert_equals(charCodeBufferToString(await slicedBlob.arrayBuffer()), "oo"); + assert_equals(charCodeArrayToString(await slicedBlob.bytes()), "oo"); const reader = slicedBlob.stream().getReader(); const { value } = await reader.read(); @@ -48,6 +49,14 @@ assert_equals(charCodeBufferToString(charCodeBuffer), "bar"); }, "arrayBuffer()"); +promise_test(async () => { + const { bytes } = await BlobPrototypeFromDetachedFramePromise; + const blob = new Blob(["bar"]); + + const charCodeBytes = await bytes.call(blob); + assert_equals(charCodeArrayToString(charCodeBytes), "bar"); +}, "bytes()"); + promise_test(async () => { const { stream } = await BlobPrototypeFromDetachedFramePromise; const blob = new Blob(["baz"]); diff --git a/LayoutTests/imported/w3c/web-platform-tests/FileAPI/blob/Blob-bytes.any-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/FileAPI/blob/Blob-bytes.any-expected.txt new file mode 100644 index 0000000000000..ecd2b05a83799 --- /dev/null +++ b/LayoutTests/imported/w3c/web-platform-tests/FileAPI/blob/Blob-bytes.any-expected.txt @@ -0,0 +1,7 @@ + +PASS Blob.bytes() +PASS Blob.bytes() empty Blob data +PASS Blob.bytes() non-ascii input +PASS Blob.bytes() non-unicode input +PASS Blob.bytes() concurrent reads + diff --git a/LayoutTests/imported/w3c/web-platform-tests/FileAPI/blob/Blob-bytes.any.html b/LayoutTests/imported/w3c/web-platform-tests/FileAPI/blob/Blob-bytes.any.html new file mode 100644 index 0000000000000..2382913528e69 --- /dev/null +++ b/LayoutTests/imported/w3c/web-platform-tests/FileAPI/blob/Blob-bytes.any.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/LayoutTests/imported/w3c/web-platform-tests/FileAPI/blob/Blob-bytes.any.js b/LayoutTests/imported/w3c/web-platform-tests/FileAPI/blob/Blob-bytes.any.js new file mode 100644 index 0000000000000..5173b3715c805 --- /dev/null +++ b/LayoutTests/imported/w3c/web-platform-tests/FileAPI/blob/Blob-bytes.any.js @@ -0,0 +1,45 @@ +// META: title=Blob bytes() +// META: script=../support/Blob.js +'use strict'; + +promise_test(async () => { + const input_arr = new TextEncoder().encode("PASS"); + const blob = new Blob([input_arr]); + const uint8array = await blob.bytes(); + assert_true(uint8array instanceof Uint8Array); + assert_equals_typed_array(uint8array, input_arr); +}, "Blob.bytes()") + +promise_test(async () => { + const input_arr = new TextEncoder().encode(""); + const blob = new Blob([input_arr]); + const uint8array = await blob.bytes(); + assert_true(uint8array instanceof Uint8Array); + assert_equals_typed_array(uint8array, input_arr); +}, "Blob.bytes() empty Blob data") + +promise_test(async () => { + const input_arr = new TextEncoder().encode("\u08B8\u000a"); + const blob = new Blob([input_arr]); + const uint8array = await blob.bytes(); + assert_equals_typed_array(uint8array, input_arr); +}, "Blob.bytes() non-ascii input") + +promise_test(async () => { + const input_arr = [8, 241, 48, 123, 151]; + const typed_arr = new Uint8Array(input_arr); + const blob = new Blob([typed_arr]); + const uint8array = await blob.bytes(); + assert_equals_typed_array(uint8array, typed_arr); +}, "Blob.bytes() non-unicode input") + +promise_test(async () => { + const input_arr = new TextEncoder().encode("PASS"); + const blob = new Blob([input_arr]); + const uint8array_results = await Promise.all([blob.bytes(), + blob.bytes(), blob.bytes()]); + for (let uint8array of uint8array_results) { + assert_true(uint8array instanceof Uint8Array); + assert_equals_typed_array(uint8array, input_arr); + } +}, "Blob.bytes() concurrent reads") diff --git a/LayoutTests/imported/w3c/web-platform-tests/FileAPI/blob/Blob-bytes.any.worker-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/FileAPI/blob/Blob-bytes.any.worker-expected.txt new file mode 100644 index 0000000000000..ecd2b05a83799 --- /dev/null +++ b/LayoutTests/imported/w3c/web-platform-tests/FileAPI/blob/Blob-bytes.any.worker-expected.txt @@ -0,0 +1,7 @@ + +PASS Blob.bytes() +PASS Blob.bytes() empty Blob data +PASS Blob.bytes() non-ascii input +PASS Blob.bytes() non-unicode input +PASS Blob.bytes() concurrent reads + diff --git a/LayoutTests/imported/w3c/web-platform-tests/FileAPI/blob/Blob-bytes.any.worker.html b/LayoutTests/imported/w3c/web-platform-tests/FileAPI/blob/Blob-bytes.any.worker.html new file mode 100644 index 0000000000000..2382913528e69 --- /dev/null +++ b/LayoutTests/imported/w3c/web-platform-tests/FileAPI/blob/Blob-bytes.any.worker.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/LayoutTests/imported/w3c/web-platform-tests/FileAPI/blob/w3c-import.log b/LayoutTests/imported/w3c/web-platform-tests/FileAPI/blob/w3c-import.log index 014ceb88aa9f3..26474fa0b3f37 100644 --- a/LayoutTests/imported/w3c/web-platform-tests/FileAPI/blob/w3c-import.log +++ b/LayoutTests/imported/w3c/web-platform-tests/FileAPI/blob/w3c-import.log @@ -15,6 +15,7 @@ None ------------------------------------------------------------------------ List of files: /LayoutTests/imported/w3c/web-platform-tests/FileAPI/blob/Blob-array-buffer.any.js +/LayoutTests/imported/w3c/web-platform-tests/FileAPI/blob/Blob-bytes.any.js /LayoutTests/imported/w3c/web-platform-tests/FileAPI/blob/Blob-constructor-dom.window.js /LayoutTests/imported/w3c/web-platform-tests/FileAPI/blob/Blob-constructor-endings.html /LayoutTests/imported/w3c/web-platform-tests/FileAPI/blob/Blob-constructor.any.js diff --git a/Source/WebCore/Modules/push-api/PushMessageData.cpp b/Source/WebCore/Modules/push-api/PushMessageData.cpp index c27e4a425ddaf..202d3382eb396 100644 --- a/Source/WebCore/Modules/push-api/PushMessageData.cpp +++ b/Source/WebCore/Modules/push-api/PushMessageData.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 Apple Inc. All rights reserved. + * Copyright (C) 2021-2024 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -40,7 +40,7 @@ WTF_MAKE_ISO_ALLOCATED_IMPL(PushMessageData); ExceptionOr> PushMessageData::arrayBuffer() { - auto buffer = ArrayBuffer::tryCreate(m_data.data(), m_data.size()); + auto buffer = ArrayBuffer::tryCreate(m_data.span()); if (!buffer) return Exception { ExceptionCode::OutOfMemoryError }; return buffer; @@ -51,6 +51,14 @@ RefPtr PushMessageData::blob(ScriptExecutionContext& context) return Blob::create(&context, Vector { m_data }, { }); } +ExceptionOr> PushMessageData::bytes() +{ + auto view = Uint8Array::tryCreate(m_data.span()); + if (!view) + return Exception { ExceptionCode::OutOfMemoryError }; + return view; +} + ExceptionOr PushMessageData::json(JSDOMGlobalObject& globalObject) { JSC::JSLockHolder lock(&globalObject); diff --git a/Source/WebCore/Modules/push-api/PushMessageData.h b/Source/WebCore/Modules/push-api/PushMessageData.h index dc00e4262cd3a..8096fd773607d 100644 --- a/Source/WebCore/Modules/push-api/PushMessageData.h +++ b/Source/WebCore/Modules/push-api/PushMessageData.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 Apple Inc. All rights reserved. + * Copyright (C) 2021-2024 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -28,6 +28,7 @@ #include "ExceptionOr.h" #include #include +#include #include #include @@ -44,6 +45,7 @@ class PushMessageData final : public RefCounted { ExceptionOr> arrayBuffer(); RefPtr blob(ScriptExecutionContext&); + ExceptionOr> bytes(); ExceptionOr json(JSDOMGlobalObject&); String text(); diff --git a/Source/WebCore/Modules/push-api/PushMessageData.idl b/Source/WebCore/Modules/push-api/PushMessageData.idl index e012e99666ebe..66e42aae640f3 100644 --- a/Source/WebCore/Modules/push-api/PushMessageData.idl +++ b/Source/WebCore/Modules/push-api/PushMessageData.idl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 Apple Inc. All rights reserved. + * Copyright (C) 2021-2024 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -29,6 +29,7 @@ ] interface PushMessageData { ArrayBuffer arrayBuffer(); [CallWith=CurrentScriptExecutionContext] Blob blob(); + Uint8Array bytes(); [CallWith=CurrentGlobalObject] any json(); USVString text(); }; diff --git a/Source/WebCore/fileapi/Blob.cpp b/Source/WebCore/fileapi/Blob.cpp index dab06cd52babb..dc63ea0eeebde 100644 --- a/Source/WebCore/fileapi/Blob.cpp +++ b/Source/WebCore/fileapi/Blob.cpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2010 Google Inc. All rights reserved. + * Copyright (C) 2012-2024 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -288,19 +289,33 @@ void Blob::text(Ref&& promise) }); } -void Blob::arrayBuffer(Ref&& promise) +static ExceptionOr> arrayBufferFromBlobLoader(BlobLoader& blobLoader) +{ + if (auto optionalErrorCode = blobLoader.errorCode()) + return Exception { *optionalErrorCode }; + RefPtr arrayBuffer = blobLoader.arrayBufferResult(); + if (!arrayBuffer) + return Exception { ExceptionCode::InvalidStateError }; + return arrayBuffer.releaseNonNull(); +} + +void Blob::arrayBuffer(DOMPromiseDeferred&& promise) { loadBlob(FileReaderLoader::ReadAsArrayBuffer, [promise = WTFMove(promise)](BlobLoader& blobLoader) mutable { - if (auto optionalErrorCode = blobLoader.errorCode()) { - promise->reject(Exception { *optionalErrorCode }); - return; - } - auto arrayBuffer = blobLoader.arrayBufferResult(); - if (!arrayBuffer) { - promise->reject(Exception { ExceptionCode::InvalidStateError }); + promise.settle(arrayBufferFromBlobLoader(blobLoader)); + }); +} + +void Blob::bytes(Ref&& promise) +{ + loadBlob(FileReaderLoader::ReadAsArrayBuffer, [promise = WTFMove(promise)](BlobLoader& blobLoader) mutable { + auto arrayBuffer = arrayBufferFromBlobLoader(blobLoader); + if (arrayBuffer.hasException()) { + promise->reject(arrayBuffer.releaseException()); return; } - promise->resolve(*arrayBuffer); + Ref view = Uint8Array::create(arrayBuffer.releaseReturnValue()); + promise->resolve(WTFMove(view)); }); } diff --git a/Source/WebCore/fileapi/Blob.h b/Source/WebCore/fileapi/Blob.h index 8bf380ab8ddb5..05b0f0760e999 100644 --- a/Source/WebCore/fileapi/Blob.h +++ b/Source/WebCore/fileapi/Blob.h @@ -1,6 +1,6 @@ /* * Copyright (C) 2010 Google Inc. All rights reserved. - * Copyright (C) 2012-2019 Apple Inc. All rights reserved. + * Copyright (C) 2012-2024 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -58,6 +58,9 @@ class ScriptExecutionContext; class FragmentedSharedBuffer; class WebCoreOpaqueRoot; +struct IDLArrayBuffer; + +template class DOMPromiseDeferred; template class ExceptionOr; using BlobPartVariant = std::variant, RefPtr, RefPtr, String>; @@ -121,7 +124,8 @@ class Blob : public ScriptWrappable, public URLRegistrable, public RefCounted slice(long long start, long long end, const String& contentType) const; void text(Ref&&); - void arrayBuffer(Ref&&); + void arrayBuffer(DOMPromiseDeferred&&); + void bytes(Ref&&); ExceptionOr> stream(); size_t memoryCost() const { return m_memoryCost; } diff --git a/Source/WebCore/fileapi/Blob.idl b/Source/WebCore/fileapi/Blob.idl index 6181387945b37..9295b9aa5798a 100644 --- a/Source/WebCore/fileapi/Blob.idl +++ b/Source/WebCore/fileapi/Blob.idl @@ -1,5 +1,6 @@ /* * Copyright (C) 2010 Google Inc. All rights reserved. + * Copyright (C) 2012-2024 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -48,4 +49,5 @@ typedef (BufferSource or Blob or USVString) BlobPart; [NewObject] ReadableStream stream(); [NewObject] Promise text(); [NewObject] Promise arrayBuffer(); + [NewObject] Promise bytes(); };