LibWeb: Reorder step to get chunk size after checking for aborted stream

This fixes a crash seen on:

http://wpt.live/streams/writable-streams/aborting.any.html

I believe this is a spec bug, which has been reported to the Streams
spec.
This commit is contained in:
Shannon Booth 2024-11-10 00:15:59 +13:00 committed by Tim Flynn
parent 3594cdf948
commit d31014d135
Notes: github-actions[bot] 2024-11-09 13:10:46 +00:00
4 changed files with 1582 additions and 1 deletions

View file

@ -0,0 +1,73 @@
Summary
Harness status: Error
Rerun
Found 62 tests
45 Pass
17 Fail
Details
Result Test Name MessagePass Aborting a WritableStream before it starts should cause the writer's unsettled ready promise to reject
Pass Aborting a WritableStream should cause the writer's fulfilled ready promise to reset to a rejected one
Pass abort() on a released writer rejects
Pass Aborting a WritableStream immediately prevents future writes
Pass Aborting a WritableStream prevents further writes after any that are in progress
Pass Fulfillment value of writer.abort() call must be undefined even if the underlying sink returns a non-undefined value
Pass WritableStream if sink's abort throws, the promise returned by writer.abort() rejects
Pass WritableStream if sink's abort throws, the promise returned by multiple writer.abort()s is the same and rejects
Pass WritableStream if sink's abort throws, the promise returned by ws.abort() rejects
Pass WritableStream if sink's abort throws, for an abort performed during a write, the promise returned by ws.abort() rejects
Pass Aborting a WritableStream passes through the given reason
Pass Aborting a WritableStream puts it in an errored state with the error passed to abort()
Pass Aborting a WritableStream causes any outstanding write() promises to be rejected with the reason supplied
Pass Closing but then immediately aborting a WritableStream causes the stream to error
Pass Closing a WritableStream and aborting it while it closes causes the stream to ignore the abort attempt
Pass Aborting a WritableStream after it is closed is a no-op
Pass WritableStream should NOT call underlying sink's close if no abort is supplied (historical)
Pass returning a thenable from abort() should work
Pass .closed should not resolve before fulfilled write()
Pass .closed should not resolve before rejected write(); write() error should not overwrite abort() error
Pass writes should be satisfied in order when aborting
Pass writes should be satisfied in order after rejected write when aborting
Pass close() should reject with abort reason why abort() is first error
Pass underlying abort() should not be called until underlying write() completes
Pass underlying abort() should not be called if underlying close() has started
Pass if underlying close() has started and then rejects, the abort() and close() promises should reject with the underlying close rejection reason
Pass an abort() that happens during a write() should trigger the underlying abort() even with a close() queued
Pass if a writer is created for a stream with a pending abort, its ready should be rejected with the abort error
Pass writer close() promise should resolve before abort() promise
Fail writer.ready should reject on controller error without waiting for underlying write
Pass writer.abort() while there is an in-flight write, and then finish the write with rejection
Fail writer.abort(), controller.error() while there is an in-flight write, and then finish the write
Fail writer.abort(), controller.error() while there is an in-flight close, and then finish the close
Fail controller.error(), writer.abort() while there is an in-flight write, and then finish the write
Fail controller.error(), writer.abort() while there is an in-flight close, and then finish the close
Pass releaseLock() while aborting should reject the original closed promise
Pass releaseLock() during delayed async abort() should reject the writer.closed promise
Pass sink abort() should not be called until sink start() is done
Fail if start attempts to error the controller after abort() has been called, then it should lose undefined is not a function (evaluated from 'controller.error')
Pass stream abort() promise should still resolve if sink start() rejects
Pass writer abort() during sink start() should replace the writer.ready promise synchronously
Pass promises returned from other writer methods should be rejected when writer abort() happens during sink start()
Fail abort() should succeed despite rejection from write
Fail abort() should be rejected with the rejection returned from close()
Pass a rejecting sink.write() should not prevent sink.abort() from being called
Pass when start errors after stream abort(), underlying sink abort() should be called anyway
Pass when calling abort() twice on the same stream, both should give the same promise that fulfills with undefined
Pass when calling abort() twice on the same stream, but sequentially so so there's no pending abort the second time, both should fulfill with undefined
Fail calling abort() on an errored stream should fulfill with undefined undefined is not a function (evaluated from 'c.error')
Fail sink abort() should not be called if stream was erroring due to controller.error() before abort() was called
Pass sink abort() should not be called if stream was erroring due to bad strategy before abort() was called
Pass abort with no arguments should set the stored error to undefined
Pass abort with an undefined argument should set the stored error to undefined
Pass abort with a string argument should set the stored error to that argument
Pass abort on a locked stream should reject
Fail WritableStreamDefaultController.signal
Fail the abort signal is signalled synchronously - write
Fail the abort signal is signalled synchronously - close
Fail the abort signal is not signalled on error
Fail the abort signal is not signalled on write failure
Fail the abort signal is not signalled on close failure
Fail recursive abort() call

View file

@ -0,0 +1,16 @@
<!doctype html>
<meta charset=utf-8>
<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>
<script src="../resources/test-utils.js"></script>
<script src="../resources/recording-streams.js"></script>
<div id=log></div>
<script src="../../streams/writable-streams/aborting.any.js"></script>

File diff suppressed because it is too large Load diff

View file

@ -4255,7 +4255,7 @@ JS::NonnullGCPtr<WebIDL::Promise> writable_stream_default_writer_write(WritableS
auto controller = stream->controller();
// 4. Let chunkSize be ! WritableStreamDefaultControllerGetChunkSize(controller, chunk).
auto chunk_size = writable_stream_default_controller_get_chunk_size(*controller, chunk);
// NOTE: See FIXME below
// 5. If stream is not equal to writer.[[stream]], return a promise rejected with a TypeError exception.
if (stream.ptr() != writer.stream().ptr()) {
@ -4280,6 +4280,11 @@ JS::NonnullGCPtr<WebIDL::Promise> writable_stream_default_writer_write(WritableS
if (state == WritableStream::State::Erroring)
return WebIDL::create_rejected_promise(realm, stream->stored_error());
// 4. Let chunkSize be ! WritableStreamDefaultControllerGetChunkSize(controller, chunk).
// FIXME: This is out of order due to a spec bug: https://github.com/whatwg/streams/issues/1331
// An abort clears the strategySizeAlgorithm, so we need to move this past the "erroring" check
auto chunk_size = writable_stream_default_controller_get_chunk_size(*controller, chunk);
// 10. Assert: state is "writable".
VERIFY(state == WritableStream::State::Writable);