LibWeb: Run text encoder/decoder stream algorithms in their realm

Temporarily enter the TextEncoderStream/TextDecoderStream realm while
running their transform and flush algorithms.

This ensures objects and exceptions created through those algorithms are
associated with the constructor realm, matching the encoding streams
realm WPT.

I find this behaviour _somewhat_ strange, and this is only very loosely
specified, but all browsers have aligned on this behviour, so we may
as well match it.
This commit is contained in:
Shannon Booth 2026-06-22 14:24:47 +02:00 committed by Shannon Booth
parent ae0c7bc097
commit 02320c9b58
3 changed files with 15 additions and 10 deletions

View file

@ -15,6 +15,7 @@
#include <LibWeb/Bindings/TextDecoder.h>
#include <LibWeb/Bindings/TextDecoderStream.h>
#include <LibWeb/Encoding/TextDecoderStream.h>
#include <LibWeb/HTML/Scripting/TemporaryExecutionContext.h>
#include <LibWeb/Streams/TransformStream.h>
#include <LibWeb/Streams/TransformStreamOperations.h>
#include <LibWeb/WebIDL/AbstractOperations.h>
@ -59,6 +60,7 @@ WebIDL::ExceptionOr<GC::Ref<TextDecoderStream>> TextDecoderStream::construct_imp
// algorithm with this and chunk.
auto transform_algorithm = GC::create_function(realm.heap(), [stream](JS::Value chunk) -> GC::Ref<WebIDL::Promise> {
auto& realm = stream->realm();
HTML::TemporaryExecutionContext execution_context { realm };
if (auto result = stream->decode_and_enqueue_chunk(chunk); result.is_error())
return WebIDL::create_rejected_promise_from_exception(realm, result.release_error());
return WebIDL::create_resolved_promise(realm, JS::js_undefined());
@ -67,6 +69,7 @@ WebIDL::ExceptionOr<GC::Ref<TextDecoderStream>> TextDecoderStream::construct_imp
// 8. Let flushAlgorithm be an algorithm which takes no arguments and runs the flush and enqueue algorithm with this.
auto flush_algorithm = GC::create_function(realm.heap(), [stream]() -> GC::Ref<WebIDL::Promise> {
auto& realm = stream->realm();
HTML::TemporaryExecutionContext execution_context { realm };
if (auto result = stream->flush_and_enqueue(); result.is_error())
return WebIDL::create_rejected_promise_from_exception(realm, result.release_error());
return WebIDL::create_resolved_promise(realm, JS::js_undefined());

View file

@ -12,6 +12,7 @@
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/Bindings/TextEncoderStream.h>
#include <LibWeb/Encoding/TextEncoderStream.h>
#include <LibWeb/HTML/Scripting/TemporaryExecutionContext.h>
#include <LibWeb/Streams/TransformStream.h>
#include <LibWeb/Streams/TransformStreamOperations.h>
#include <LibWeb/WebIDL/Promise.h>
@ -38,6 +39,7 @@ GC::Ref<TextEncoderStream> TextEncoderStream::construct_impl(JS::Realm& realm)
auto transform_algorithm = GC::create_function(realm.heap(), [stream](JS::Value chunk) -> GC::Ref<WebIDL::Promise> {
auto& realm = stream->realm();
auto& vm = realm.vm();
HTML::TemporaryExecutionContext execution_context { realm };
if (auto result = stream->encode_and_enqueue_chunk(chunk); result.is_error()) {
auto throw_completion = Bindings::exception_to_throw_completion(vm, result.exception());
@ -51,6 +53,7 @@ GC::Ref<TextEncoderStream> TextEncoderStream::construct_impl(JS::Realm& realm)
auto flush_algorithm = GC::create_function(realm.heap(), [stream]() -> GC::Ref<WebIDL::Promise> {
auto& realm = stream->realm();
auto& vm = realm.vm();
HTML::TemporaryExecutionContext execution_context { realm };
if (auto result = stream->encode_and_flush(); result.is_error()) {
auto throw_completion = Bindings::exception_to_throw_completion(vm, result.exception());

View file

@ -2,17 +2,16 @@ Harness status: OK
Found 12 tests
4 Pass
8 Fail
12 Pass
Pass a TextEncoderStream object should be associated with the realm the constructor came from
Pass TextEncoderStream's readable and writable attributes should come from the same realm as the constructor definition
Fail the output chunks when read is called after write should come from the same realm as the constructor of TextEncoderStream
Fail the output chunks when write is called with a pending read should come from the same realm as the constructor of TextEncoderStream
Fail TypeError for unconvertable chunk should come from constructor realm of TextEncoderStream
Pass the output chunks when read is called after write should come from the same realm as the constructor of TextEncoderStream
Pass the output chunks when write is called with a pending read should come from the same realm as the constructor of TextEncoderStream
Pass TypeError for unconvertable chunk should come from constructor realm of TextEncoderStream
Pass a TextDecoderStream object should be associated with the realm the constructor came from
Pass TextDecoderStream's readable and writable attributes should come from the same realm as the constructor definition
Fail the result object when read is called after write should come from the same realm as the constructor of TextDecoderStream
Fail the result object when write is called with a pending read should come from the same realm as the constructor of TextDecoderStream
Fail TypeError for chunk with the wrong type should come from constructor realm of TextDecoderStream
Fail TypeError for invalid chunk should come from constructor realm of TextDecoderStream
Fail TypeError for incomplete input should come from constructor realm of TextDecoderStream
Pass the result object when read is called after write should come from the same realm as the constructor of TextDecoderStream
Pass the result object when write is called with a pending read should come from the same realm as the constructor of TextDecoderStream
Pass TypeError for chunk with the wrong type should come from constructor realm of TextDecoderStream
Pass TypeError for invalid chunk should come from constructor realm of TextDecoderStream
Pass TypeError for incomplete input should come from constructor realm of TextDecoderStream