script: allow for undefined chunks in stream piping (#38470)

Current code uses `undefined` as chunk value to identify the closing of
a stream, but this breaks once you start streaming a chunk that is
actually `undefined`, as shown in
https://github.com/servo/servo/pull/38466. This PR re-implement the
logic in a way that allows for chunks to be `undefined`.

Testing: Should maintain `streams/piping` WPT pass rates. Also makes the
`undefined` case of
[`/encoding/streams/encode-bad-chunks.any.js`](c59ee57b5d/tests/wpt/tests/encoding/streams/encode-bad-chunks.any.js (L29)),
but that is only noticeable in https://github.com/servo/servo/pull/38466
Fixes: None, but noted in https://github.com/servo/servo/pull/38466

Signed-off-by: gterzian <2792687+gterzian@users.noreply.github.com>
This commit is contained in:
Gregory Terzian 2025-08-05 05:42:25 +08:00 committed by GitHub
parent 778dc70181
commit 7ad32f944f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -249,28 +249,7 @@ impl Callback for PipeTo {
// If dest.[[state]] is "writable", // If dest.[[state]] is "writable",
// and ! WritableStreamCloseQueuedOrInFlight(dest) is false, // and ! WritableStreamCloseQueuedOrInFlight(dest) is false,
if dest.is_writable() && !dest.close_queued_or_in_flight() { if dest.is_writable() && !dest.close_queued_or_in_flight() {
let has_done = { let Ok(done) = get_read_promise_done(cx, &result, can_gc) else {
if !result.is_object() {
false
} else {
rooted!(in(*cx) let object = result.to_object());
rooted!(in(*cx) let mut done = UndefinedValue());
unsafe {
get_dictionary_property(
*cx,
object.handle(),
"done",
done.handle_mut(),
can_gc,
)
.unwrap()
}
}
};
// If any chunks have been read but not yet written, write them to dest.
let contained_bytes = self.write_chunk(cx, &global, result, can_gc);
if !contained_bytes && !has_done {
// This is the case that the microtask ran in reaction // This is the case that the microtask ran in reaction
// to the closed promise of the reader, // to the closed promise of the reader,
// so we should wait for subsequent chunks, // so we should wait for subsequent chunks,
@ -278,6 +257,11 @@ impl Callback for PipeTo {
// (reader is closed, but there are still pending reads). // (reader is closed, but there are still pending reads).
// Shutdown will happen when the last chunk has been received. // Shutdown will happen when the last chunk has been received.
return; return;
};
if !done {
// If any chunks have been read but not yet written, write them to dest.
self.write_chunk(cx, &global, result, can_gc);
} }
} }
} }
@ -446,7 +430,7 @@ impl PipeTo {
get_dictionary_property(*cx, object.handle(), "value", bytes.handle_mut(), can_gc) get_dictionary_property(*cx, object.handle(), "value", bytes.handle_mut(), can_gc)
.expect("Chunk should have a value.") .expect("Chunk should have a value.")
}; };
if !bytes.is_undefined() && has_value { if has_value {
// Write the chunk. // Write the chunk.
let write_promise = self.writer.write(cx, global, bytes.handle(), can_gc); let write_promise = self.writer.write(cx, global, bytes.handle(), can_gc);
self.pending_writes.borrow_mut().push_back(write_promise); self.pending_writes.borrow_mut().push_back(write_promise);