Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Blob is missing text() & arrayBuffer() operations
https://bugs.webkit.org/show_bug.cgi?id=215663

Reviewed by Geoff Garen.

LayoutTests/imported/w3c:

* web-platform-tests/FileAPI/blob/Blob-array-buffer.any-expected.txt:
* web-platform-tests/FileAPI/blob/Blob-array-buffer.any.worker-expected.txt:
* web-platform-tests/FileAPI/blob/Blob-text.any-expected.txt:
* web-platform-tests/FileAPI/blob/Blob-text.any.worker-expected.txt:
* web-platform-tests/FileAPI/idlharness-expected.txt:
* web-platform-tests/FileAPI/idlharness.worker-expected.txt:

Source/WebCore:

Implementation support for Blob.text() & Blob.arrayBuffer() as per specification:
- https://w3c.github.io/FileAPI/#blob-section

The implementation relies on the pre-existing BlobLoader class but the following
changes were made:
- Stop calling start() in the BlobLoader constructor and have the caller call
  start() explicitly after constructing the BlobLoader. This is important because
  the load may fail synchronously in some cases.
- Add support for reading Blob as text. Previously, BlobLoader would only support
  reading the blob as an ArrayBuffer.
- Use a CompletionHandler instead of a Function and make sure that it is always
  called.

No new tests, rebaselined existing tests.

* fileapi/Blob.cpp:
(WebCore::Blob::~Blob):
(WebCore::Blob::loadBlob):
(WebCore::Blob::text):
(WebCore::Blob::arrayBuffer):
* fileapi/Blob.h:
* fileapi/Blob.idl:
* fileapi/BlobLoader.h:
(WebCore::BlobLoader::BlobLoader):
(WebCore::BlobLoader::~BlobLoader):
(WebCore::BlobLoader::cancel):
(WebCore::BlobLoader::start):
(WebCore::BlobLoader::didFinishLoading):
(WebCore::BlobLoader::didFail):
(WebCore::BlobLoader::complete): Deleted.
* fileapi/NetworkSendQueue.cpp:
(WebCore::NetworkSendQueue::enqueue):
(WebCore::NetworkSendQueue::clear):
(WebCore::NetworkSendQueue::processMessages):
* page/ShareDataReader.cpp:
(WebCore::ShareDataReader::start):
(WebCore::ShareDataReader::didFinishLoading):
(WebCore::ShareDataReader::cancel):


Canonical link: https://commits.webkit.org/228429@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@265908 268f45cc-cd09-0410-ab3c-d52691b4dbfc
  • Loading branch information
cdumez committed Aug 19, 2020
1 parent d1e50e3 commit 1e0d92d
Show file tree
Hide file tree
Showing 18 changed files with 206 additions and 75 deletions.
14 changes: 14 additions & 0 deletions LayoutTests/imported/w3c/ChangeLog
@@ -1,3 +1,17 @@
2020-08-19 Chris Dumez <cdumez@apple.com>

Blob is missing text() & arrayBuffer() operations
https://bugs.webkit.org/show_bug.cgi?id=215663

Reviewed by Geoff Garen.

* web-platform-tests/FileAPI/blob/Blob-array-buffer.any-expected.txt:
* web-platform-tests/FileAPI/blob/Blob-array-buffer.any.worker-expected.txt:
* web-platform-tests/FileAPI/blob/Blob-text.any-expected.txt:
* web-platform-tests/FileAPI/blob/Blob-text.any.worker-expected.txt:
* web-platform-tests/FileAPI/idlharness-expected.txt:
* web-platform-tests/FileAPI/idlharness.worker-expected.txt:

2020-08-19 Chris Dumez <cdumez@apple.com>

Unreviewed, rebaseline imported/w3c/web-platform-tests/webaudio/the-audio-api/the-oscillatornode-interface/osc-basic-waveform.html
Expand Down
@@ -1,7 +1,7 @@

FAIL Blob.arrayBuffer() promise_test: Unhandled rejection with value: object "TypeError: blob.arrayBuffer is not a function. (In 'blob.arrayBuffer()', 'blob.arrayBuffer' is undefined)"
FAIL Blob.arrayBuffer() empty Blob data promise_test: Unhandled rejection with value: object "TypeError: blob.arrayBuffer is not a function. (In 'blob.arrayBuffer()', 'blob.arrayBuffer' is undefined)"
FAIL Blob.arrayBuffer() non-ascii input promise_test: Unhandled rejection with value: object "TypeError: blob.arrayBuffer is not a function. (In 'blob.arrayBuffer()', 'blob.arrayBuffer' is undefined)"
FAIL Blob.arrayBuffer() non-unicode input promise_test: Unhandled rejection with value: object "TypeError: blob.arrayBuffer is not a function. (In 'blob.arrayBuffer()', 'blob.arrayBuffer' is undefined)"
FAIL Blob.arrayBuffer() concurrent reads promise_test: Unhandled rejection with value: object "TypeError: blob.arrayBuffer is not a function. (In 'blob.arrayBuffer()', 'blob.arrayBuffer' is undefined)"
PASS Blob.arrayBuffer()
PASS Blob.arrayBuffer() empty Blob data
PASS Blob.arrayBuffer() non-ascii input
PASS Blob.arrayBuffer() non-unicode input
PASS Blob.arrayBuffer() concurrent reads

@@ -1,7 +1,7 @@

FAIL Blob.arrayBuffer() promise_test: Unhandled rejection with value: object "TypeError: blob.arrayBuffer is not a function. (In 'blob.arrayBuffer()', 'blob.arrayBuffer' is undefined)"
FAIL Blob.arrayBuffer() empty Blob data promise_test: Unhandled rejection with value: object "TypeError: blob.arrayBuffer is not a function. (In 'blob.arrayBuffer()', 'blob.arrayBuffer' is undefined)"
FAIL Blob.arrayBuffer() non-ascii input promise_test: Unhandled rejection with value: object "TypeError: blob.arrayBuffer is not a function. (In 'blob.arrayBuffer()', 'blob.arrayBuffer' is undefined)"
FAIL Blob.arrayBuffer() non-unicode input promise_test: Unhandled rejection with value: object "TypeError: blob.arrayBuffer is not a function. (In 'blob.arrayBuffer()', 'blob.arrayBuffer' is undefined)"
FAIL Blob.arrayBuffer() concurrent reads promise_test: Unhandled rejection with value: object "TypeError: blob.arrayBuffer is not a function. (In 'blob.arrayBuffer()', 'blob.arrayBuffer' is undefined)"
PASS Blob.arrayBuffer()
PASS Blob.arrayBuffer() empty Blob data
PASS Blob.arrayBuffer() non-ascii input
PASS Blob.arrayBuffer() non-unicode input
PASS Blob.arrayBuffer() concurrent reads

@@ -1,10 +1,10 @@

FAIL Blob.text() promise_test: Unhandled rejection with value: object "TypeError: blob.text is not a function. (In 'blob.text()', 'blob.text' is undefined)"
FAIL Blob.text() empty blob data promise_test: Unhandled rejection with value: object "TypeError: blob.text is not a function. (In 'blob.text()', 'blob.text' is undefined)"
FAIL Blob.text() multi-element array in constructor promise_test: Unhandled rejection with value: object "TypeError: blob.text is not a function. (In 'blob.text()', 'blob.text' is undefined)"
FAIL Blob.text() non-unicode promise_test: Unhandled rejection with value: object "TypeError: blob.text is not a function. (In 'blob.text()', 'blob.text' is undefined)"
FAIL Blob.text() different charset param in type option promise_test: Unhandled rejection with value: object "TypeError: blob.text is not a function. (In 'blob.text()', 'blob.text' is undefined)"
FAIL Blob.text() different charset param with non-ascii input promise_test: Unhandled rejection with value: object "TypeError: blob.text is not a function. (In 'blob.text()', 'blob.text' is undefined)"
FAIL Blob.text() invalid utf-8 input promise_test: Unhandled rejection with value: object "TypeError: blob.text is not a function. (In 'blob.text()', 'blob.text' is undefined)"
FAIL Blob.text() concurrent reads promise_test: Unhandled rejection with value: object "TypeError: blob.text is not a function. (In 'blob.text()', 'blob.text' is undefined)"
PASS Blob.text()
PASS Blob.text() empty blob data
PASS Blob.text() multi-element array in constructor
PASS Blob.text() non-unicode
PASS Blob.text() different charset param in type option
PASS Blob.text() different charset param with non-ascii input
PASS Blob.text() invalid utf-8 input
PASS Blob.text() concurrent reads

@@ -1,10 +1,10 @@

FAIL Blob.text() promise_test: Unhandled rejection with value: object "TypeError: blob.text is not a function. (In 'blob.text()', 'blob.text' is undefined)"
FAIL Blob.text() empty blob data promise_test: Unhandled rejection with value: object "TypeError: blob.text is not a function. (In 'blob.text()', 'blob.text' is undefined)"
FAIL Blob.text() multi-element array in constructor promise_test: Unhandled rejection with value: object "TypeError: blob.text is not a function. (In 'blob.text()', 'blob.text' is undefined)"
FAIL Blob.text() non-unicode promise_test: Unhandled rejection with value: object "TypeError: blob.text is not a function. (In 'blob.text()', 'blob.text' is undefined)"
FAIL Blob.text() different charset param in type option promise_test: Unhandled rejection with value: object "TypeError: blob.text is not a function. (In 'blob.text()', 'blob.text' is undefined)"
FAIL Blob.text() different charset param with non-ascii input promise_test: Unhandled rejection with value: object "TypeError: blob.text is not a function. (In 'blob.text()', 'blob.text' is undefined)"
FAIL Blob.text() invalid utf-8 input promise_test: Unhandled rejection with value: object "TypeError: blob.text is not a function. (In 'blob.text()', 'blob.text' is undefined)"
FAIL Blob.text() concurrent reads promise_test: Unhandled rejection with value: object "TypeError: blob.text is not a function. (In 'blob.text()', 'blob.text' is undefined)"
PASS Blob.text()
PASS Blob.text() empty blob data
PASS Blob.text() multi-element array in constructor
PASS Blob.text() non-unicode
PASS Blob.text() different charset param in type option
PASS Blob.text() different charset param with non-ascii input
PASS Blob.text() invalid utf-8 input
PASS Blob.text() concurrent reads

Expand Up @@ -16,17 +16,17 @@ PASS Blob interface: attribute size
PASS Blob interface: attribute type
PASS Blob interface: operation slice(optional long long, optional long long, optional DOMString)
FAIL Blob interface: operation stream() assert_own_property: interface prototype object missing non-static operation expected property "stream" missing
FAIL Blob interface: operation text() assert_own_property: interface prototype object missing non-static operation expected property "text" missing
FAIL Blob interface: operation arrayBuffer() assert_own_property: interface prototype object missing non-static operation expected property "arrayBuffer" missing
PASS Blob interface: operation text()
PASS Blob interface: operation arrayBuffer()
PASS Blob must be primary interface of new Blob(["TEST"])
PASS Stringification of new Blob(["TEST"])
PASS Blob interface: new Blob(["TEST"]) must inherit property "size" with the proper type
PASS Blob interface: new Blob(["TEST"]) must inherit property "type" with the proper type
PASS Blob interface: new Blob(["TEST"]) must inherit property "slice(optional long long, optional long long, optional DOMString)" with the proper type
PASS Blob interface: calling slice(optional long long, optional long long, optional DOMString) on new Blob(["TEST"]) with too few arguments must throw TypeError
FAIL Blob interface: new Blob(["TEST"]) must inherit property "stream()" with the proper type assert_inherits: property "stream" not found in prototype chain
FAIL Blob interface: new Blob(["TEST"]) must inherit property "text()" with the proper type assert_inherits: property "text" not found in prototype chain
FAIL Blob interface: new Blob(["TEST"]) must inherit property "arrayBuffer()" with the proper type assert_inherits: property "arrayBuffer" not found in prototype chain
PASS Blob interface: new Blob(["TEST"]) must inherit property "text()" with the proper type
PASS Blob interface: new Blob(["TEST"]) must inherit property "arrayBuffer()" with the proper type
PASS File interface: existence and properties of interface object
PASS File interface object length
PASS File interface object name
Expand All @@ -44,8 +44,8 @@ PASS Blob interface: new File(["myFileBits"], "myFileName") must inherit propert
PASS Blob interface: new File(["myFileBits"], "myFileName") must inherit property "slice(optional long long, optional long long, optional DOMString)" with the proper type
PASS Blob interface: calling slice(optional long long, optional long long, optional DOMString) on new File(["myFileBits"], "myFileName") with too few arguments must throw TypeError
FAIL Blob interface: new File(["myFileBits"], "myFileName") must inherit property "stream()" with the proper type assert_inherits: property "stream" not found in prototype chain
FAIL Blob interface: new File(["myFileBits"], "myFileName") must inherit property "text()" with the proper type assert_inherits: property "text" not found in prototype chain
FAIL Blob interface: new File(["myFileBits"], "myFileName") must inherit property "arrayBuffer()" with the proper type assert_inherits: property "arrayBuffer" not found in prototype chain
PASS Blob interface: new File(["myFileBits"], "myFileName") must inherit property "text()" with the proper type
PASS Blob interface: new File(["myFileBits"], "myFileName") must inherit property "arrayBuffer()" with the proper type
PASS FileList interface: existence and properties of interface object
PASS FileList interface object length
PASS FileList interface object name
Expand Down
Expand Up @@ -14,17 +14,17 @@ PASS Blob interface: attribute size
PASS Blob interface: attribute type
PASS Blob interface: operation slice(optional long long, optional long long, optional DOMString)
FAIL Blob interface: operation stream() assert_own_property: interface prototype object missing non-static operation expected property "stream" missing
FAIL Blob interface: operation text() assert_own_property: interface prototype object missing non-static operation expected property "text" missing
FAIL Blob interface: operation arrayBuffer() assert_own_property: interface prototype object missing non-static operation expected property "arrayBuffer" missing
PASS Blob interface: operation text()
PASS Blob interface: operation arrayBuffer()
PASS Blob must be primary interface of new Blob(["TEST"])
PASS Stringification of new Blob(["TEST"])
PASS Blob interface: new Blob(["TEST"]) must inherit property "size" with the proper type
PASS Blob interface: new Blob(["TEST"]) must inherit property "type" with the proper type
PASS Blob interface: new Blob(["TEST"]) must inherit property "slice(optional long long, optional long long, optional DOMString)" with the proper type
PASS Blob interface: calling slice(optional long long, optional long long, optional DOMString) on new Blob(["TEST"]) with too few arguments must throw TypeError
FAIL Blob interface: new Blob(["TEST"]) must inherit property "stream()" with the proper type assert_inherits: property "stream" not found in prototype chain
FAIL Blob interface: new Blob(["TEST"]) must inherit property "text()" with the proper type assert_inherits: property "text" not found in prototype chain
FAIL Blob interface: new Blob(["TEST"]) must inherit property "arrayBuffer()" with the proper type assert_inherits: property "arrayBuffer" not found in prototype chain
PASS Blob interface: new Blob(["TEST"]) must inherit property "text()" with the proper type
PASS Blob interface: new Blob(["TEST"]) must inherit property "arrayBuffer()" with the proper type
PASS File interface: existence and properties of interface object
PASS File interface object length
PASS File interface object name
Expand All @@ -42,8 +42,8 @@ PASS Blob interface: new File(["myFileBits"], "myFileName") must inherit propert
PASS Blob interface: new File(["myFileBits"], "myFileName") must inherit property "slice(optional long long, optional long long, optional DOMString)" with the proper type
PASS Blob interface: calling slice(optional long long, optional long long, optional DOMString) on new File(["myFileBits"], "myFileName") with too few arguments must throw TypeError
FAIL Blob interface: new File(["myFileBits"], "myFileName") must inherit property "stream()" with the proper type assert_inherits: property "stream" not found in prototype chain
FAIL Blob interface: new File(["myFileBits"], "myFileName") must inherit property "text()" with the proper type assert_inherits: property "text" not found in prototype chain
FAIL Blob interface: new File(["myFileBits"], "myFileName") must inherit property "arrayBuffer()" with the proper type assert_inherits: property "arrayBuffer" not found in prototype chain
PASS Blob interface: new File(["myFileBits"], "myFileName") must inherit property "text()" with the proper type
PASS Blob interface: new File(["myFileBits"], "myFileName") must inherit property "arrayBuffer()" with the proper type
PASS FileList interface: existence and properties of interface object
PASS FileList interface object length
PASS FileList interface object name
Expand Down
@@ -1,3 +1,3 @@

FAIL Blobs stay alive after their records are deleted. record.a0.text is not a function. (In 'record.a0.text()', 'record.a0.text' is undefined)
PASS Blobs stay alive after their records are deleted.

@@ -1,3 +1,3 @@

FAIL Blobs stay alive after their records are deleted. record.a0.text is not a function. (In 'record.a0.text()', 'record.a0.text' is undefined)
PASS Blobs stay alive after their records are deleted.

@@ -1,3 +1,3 @@

FAIL Blobs can be read back before their records are committed. record.a0.text is not a function. (In 'record.a0.text()', 'record.a0.text' is undefined)
PASS Blobs can be read back before their records are committed.

@@ -1,3 +1,3 @@

FAIL Blobs can be read back before their records are committed. record.a0.text is not a function. (In 'record.a0.text()', 'record.a0.text' is undefined)
PASS Blobs can be read back before their records are committed.

46 changes: 46 additions & 0 deletions Source/WebCore/ChangeLog
@@ -1,3 +1,49 @@
2020-08-19 Chris Dumez <cdumez@apple.com>

Blob is missing text() & arrayBuffer() operations
https://bugs.webkit.org/show_bug.cgi?id=215663

Reviewed by Geoff Garen.

Implementation support for Blob.text() & Blob.arrayBuffer() as per specification:
- https://w3c.github.io/FileAPI/#blob-section

The implementation relies on the pre-existing BlobLoader class but the following
changes were made:
- Stop calling start() in the BlobLoader constructor and have the caller call
start() explicitly after constructing the BlobLoader. This is important because
the load may fail synchronously in some cases.
- Add support for reading Blob as text. Previously, BlobLoader would only support
reading the blob as an ArrayBuffer.
- Use a CompletionHandler instead of a Function and make sure that it is always
called.

No new tests, rebaselined existing tests.

* fileapi/Blob.cpp:
(WebCore::Blob::~Blob):
(WebCore::Blob::loadBlob):
(WebCore::Blob::text):
(WebCore::Blob::arrayBuffer):
* fileapi/Blob.h:
* fileapi/Blob.idl:
* fileapi/BlobLoader.h:
(WebCore::BlobLoader::BlobLoader):
(WebCore::BlobLoader::~BlobLoader):
(WebCore::BlobLoader::cancel):
(WebCore::BlobLoader::start):
(WebCore::BlobLoader::didFinishLoading):
(WebCore::BlobLoader::didFail):
(WebCore::BlobLoader::complete): Deleted.
* fileapi/NetworkSendQueue.cpp:
(WebCore::NetworkSendQueue::enqueue):
(WebCore::NetworkSendQueue::clear):
(WebCore::NetworkSendQueue::processMessages):
* page/ShareDataReader.cpp:
(WebCore::ShareDataReader::start):
(WebCore::ShareDataReader::didFinishLoading):
(WebCore::ShareDataReader::cancel):

2020-08-19 Alexey Shvayka <shvaikalesh@gmail.com>

Introduce OpIsCallable bytecode and intrinsic
Expand Down
42 changes: 42 additions & 0 deletions Source/WebCore/fileapi/Blob.cpp
Expand Up @@ -32,9 +32,11 @@
#include "Blob.h"

#include "BlobBuilder.h"
#include "BlobLoader.h"
#include "BlobPart.h"
#include "BlobURL.h"
#include "File.h"
#include "JSDOMPromiseDeferred.h"
#include "ScriptExecutionContext.h"
#include "SharedBuffer.h"
#include "ThreadableBlobRegistry.h"
Expand Down Expand Up @@ -153,6 +155,9 @@ Blob::Blob(const URL& srcURL, long long start, long long end, const String& type

Blob::~Blob()
{
while (!m_blobLoaders.isEmpty())
(*m_blobLoaders.begin())->cancel();

ThreadableBlobRegistry::unregisterBlobURL(m_internalURL);
}

Expand Down Expand Up @@ -186,6 +191,43 @@ String Blob::normalizedContentType(const String& contentType)
return contentType.convertToASCIILowercase();
}

void Blob::loadBlob(ScriptExecutionContext& context, FileReaderLoader::ReadType readType, CompletionHandler<void(std::unique_ptr<BlobLoader>)>&& completionHandler)
{
auto blobLoader = makeUnique<BlobLoader>([this, completionHandler = WTFMove(completionHandler)](BlobLoader& blobLoader) mutable {
completionHandler(m_blobLoaders.take(&blobLoader));
});
auto* blobLoaderPtr = blobLoader.get();
m_blobLoaders.add(WTFMove(blobLoader));
blobLoaderPtr->start(*this, &context, readType);
}

void Blob::text(ScriptExecutionContext& context, Ref<DeferredPromise>&& promise)
{
loadBlob(context, FileReaderLoader::ReadAsText, [promise = WTFMove(promise)](std::unique_ptr<BlobLoader> blobLoader) mutable {
if (auto optionalErrorCode = blobLoader->errorCode()) {
promise->reject(Exception { *optionalErrorCode });
return;
}
promise->resolve<IDLDOMString>(blobLoader->stringResult());
});
}

void Blob::arrayBuffer(ScriptExecutionContext& context, Ref<DeferredPromise>&& promise)
{
loadBlob(context, FileReaderLoader::ReadAsArrayBuffer, [promise = WTFMove(promise)](std::unique_ptr<BlobLoader> blobLoader) mutable {
if (auto optionalErrorCode = blobLoader->errorCode()) {
promise->reject(Exception { *optionalErrorCode });
return;
}
auto arrayBuffer = blobLoader->arrayBufferResult();
if (!arrayBuffer) {
promise->reject(Exception { InvalidStateError });
return;
}
promise->resolve<IDLArrayBuffer>(*arrayBuffer);
});
}

#if ASSERT_ENABLED
bool Blob::isNormalizedContentType(const String& contentType)
{
Expand Down

0 comments on commit 1e0d92d

Please sign in to comment.