mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-08-06 08:10:02 +00:00
LibWeb/FileAPI: Handle an aborted stream in Blob::get_stream() close
The streams AO that we were calling to close the stream would assert if it was not in a readable state. This version of close is exported publicly in the streams specification, and properly handles this situation. Fixes a crash in the imported test, and happens to fix some others!
This commit is contained in:
parent
667a568526
commit
caf959f06c
Notes:
github-actions[bot]
2025-05-31 13:14:02 +00:00
Author: https://github.com/shannonbooth
Commit: caf959f06c
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/4941
5 changed files with 132 additions and 9 deletions
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* Copyright (c) 2022-2024, Kenneth Myhra <kennethmyhra@serenityos.org>
|
||||
* Copyright (c) 2023, Shannon Booth <shannon@serenityos.org>
|
||||
* Copyright (c) 2023-2025, Shannon Booth <shannon@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
@ -357,7 +357,7 @@ GC::Ref<Streams::ReadableStream> Blob::get_stream()
|
|||
// FIXME: Spec bug: https://github.com/w3c/FileAPI/issues/206
|
||||
//
|
||||
// We need to close the stream so that the stream will finish reading.
|
||||
Streams::readable_stream_close(*stream);
|
||||
stream->close();
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
Harness status: OK
|
||||
|
||||
Found 18 tests
|
||||
|
||||
18 Pass
|
||||
Pass Calling arrayBuffer() on an aborted request
|
||||
Pass Aborting a request after calling arrayBuffer()
|
||||
Pass Calling arrayBuffer() on an aborted consumed empty request
|
||||
Pass Calling arrayBuffer() on an aborted consumed nonempty request
|
||||
Pass Calling blob() on an aborted request
|
||||
Pass Aborting a request after calling blob()
|
||||
Pass Calling blob() on an aborted consumed empty request
|
||||
Pass Calling blob() on an aborted consumed nonempty request
|
||||
Pass Calling formData() on an aborted request
|
||||
Pass Aborting a request after calling formData()
|
||||
Pass Calling formData() on an aborted consumed nonempty request
|
||||
Pass Calling json() on an aborted request
|
||||
Pass Aborting a request after calling json()
|
||||
Pass Calling json() on an aborted consumed nonempty request
|
||||
Pass Calling text() on an aborted request
|
||||
Pass Aborting a request after calling text()
|
||||
Pass Calling text() on an aborted consumed empty request
|
||||
Pass Calling text() on an aborted consumed nonempty request
|
|
@ -2,8 +2,8 @@ Harness status: OK
|
|||
|
||||
Found 40 tests
|
||||
|
||||
28 Pass
|
||||
12 Fail
|
||||
33 Pass
|
||||
7 Fail
|
||||
Pass Consume response's body: from text to text
|
||||
Pass Consume response's body: from text to blob
|
||||
Pass Consume response's body: from text to arrayBuffer
|
||||
|
@ -13,13 +13,13 @@ Fail Consume response's body: from text with correct multipart type to formData
|
|||
Pass Consume response's body: from text without correct multipart type to formData (error case)
|
||||
Pass Consume response's body: from text with correct urlencoded type to formData
|
||||
Pass Consume response's body: from text without correct urlencoded type to formData (error case)
|
||||
Fail Consume response's body: from blob to blob
|
||||
Fail Consume response's body: from blob to text
|
||||
Fail Consume response's body: from blob to arrayBuffer
|
||||
Fail Consume response's body: from blob to json
|
||||
Pass Consume response's body: from blob to blob
|
||||
Pass Consume response's body: from blob to text
|
||||
Pass Consume response's body: from blob to arrayBuffer
|
||||
Pass Consume response's body: from blob to json
|
||||
Fail Consume response's body: from blob with correct multipart type to formData
|
||||
Pass Consume response's body: from blob without correct multipart type to formData (error case)
|
||||
Fail Consume response's body: from blob with correct urlencoded type to formData
|
||||
Pass Consume response's body: from blob with correct urlencoded type to formData
|
||||
Pass Consume response's body: from blob without correct urlencoded type to formData (error case)
|
||||
Pass Consume response's body: from FormData to formData
|
||||
Pass Consume response's body: from FormData without correct type to formData (error case)
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
<!doctype html>
|
||||
<meta charset=utf-8>
|
||||
<meta name="timeout" content="long">
|
||||
<script>
|
||||
self.GLOBAL = {
|
||||
isWindow: function() { return true; },
|
||||
isWorker: function() { return false; },
|
||||
isShadowRealm: function() { return false; },
|
||||
};
|
||||
</script>
|
||||
<script src="../../../resources/testharness.js"></script>
|
||||
<script src="../../../resources/testharnessreport.js"></script>
|
||||
|
||||
<div id=log></div>
|
||||
<script src="../../../fetch/api/abort/request.any.js"></script>
|
|
@ -0,0 +1,85 @@
|
|||
// META: timeout=long
|
||||
// META: global=window,worker
|
||||
|
||||
const BODY_FUNCTION_AND_DATA = {
|
||||
arrayBuffer: null,
|
||||
blob: null,
|
||||
formData: new FormData(),
|
||||
json: new Blob(["{}"]),
|
||||
text: null,
|
||||
};
|
||||
|
||||
for (const [bodyFunction, body] of Object.entries(BODY_FUNCTION_AND_DATA)) {
|
||||
promise_test(async () => {
|
||||
const controller = new AbortController();
|
||||
const signal = controller.signal;
|
||||
const request = new Request("../resources/data.json", {
|
||||
method: "post",
|
||||
signal,
|
||||
body,
|
||||
});
|
||||
|
||||
controller.abort();
|
||||
await request[bodyFunction]();
|
||||
assert_true(
|
||||
true,
|
||||
`An aborted request should still be able to run ${bodyFunction}()`
|
||||
);
|
||||
}, `Calling ${bodyFunction}() on an aborted request`);
|
||||
|
||||
promise_test(async () => {
|
||||
const controller = new AbortController();
|
||||
const signal = controller.signal;
|
||||
const request = new Request("../resources/data.json", {
|
||||
method: "post",
|
||||
signal,
|
||||
body,
|
||||
});
|
||||
|
||||
const p = request[bodyFunction]();
|
||||
controller.abort();
|
||||
await p;
|
||||
assert_true(
|
||||
true,
|
||||
`An aborted request should still be able to run ${bodyFunction}()`
|
||||
);
|
||||
}, `Aborting a request after calling ${bodyFunction}()`);
|
||||
|
||||
if (!body) {
|
||||
promise_test(async () => {
|
||||
const controller = new AbortController();
|
||||
const signal = controller.signal;
|
||||
const request = new Request("../resources/data.json", {
|
||||
method: "post",
|
||||
signal,
|
||||
body,
|
||||
});
|
||||
|
||||
// consuming happens synchronously, so don't wait
|
||||
fetch(request).catch(() => {});
|
||||
|
||||
controller.abort();
|
||||
await request[bodyFunction]();
|
||||
assert_true(
|
||||
true,
|
||||
`An aborted consumed request should still be able to run ${bodyFunction}() when empty`
|
||||
);
|
||||
}, `Calling ${bodyFunction}() on an aborted consumed empty request`);
|
||||
}
|
||||
|
||||
promise_test(async t => {
|
||||
const controller = new AbortController();
|
||||
const signal = controller.signal;
|
||||
const request = new Request("../resources/data.json", {
|
||||
method: "post",
|
||||
signal,
|
||||
body: body || new Blob(["foo"]),
|
||||
});
|
||||
|
||||
// consuming happens synchronously, so don't wait
|
||||
fetch(request).catch(() => {});
|
||||
|
||||
controller.abort();
|
||||
await promise_rejects_js(t, TypeError, request[bodyFunction]());
|
||||
}, `Calling ${bodyFunction}() on an aborted consumed nonempty request`);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue