From 02320c9b584581fcfe135f26aa74ca1b074119b9 Mon Sep 17 00:00:00 2001 From: Shannon Booth Date: Mon, 22 Jun 2026 14:24:47 +0200 Subject: [PATCH] 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. --- .../LibWeb/Encoding/TextDecoderStream.cpp | 3 +++ .../LibWeb/Encoding/TextEncoderStream.cpp | 3 +++ .../encoding/streams/realms.window.txt | 19 +++++++++---------- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/Libraries/LibWeb/Encoding/TextDecoderStream.cpp b/Libraries/LibWeb/Encoding/TextDecoderStream.cpp index d69d261279..7aeda02506 100644 --- a/Libraries/LibWeb/Encoding/TextDecoderStream.cpp +++ b/Libraries/LibWeb/Encoding/TextDecoderStream.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -59,6 +60,7 @@ WebIDL::ExceptionOr> TextDecoderStream::construct_imp // algorithm with this and chunk. auto transform_algorithm = GC::create_function(realm.heap(), [stream](JS::Value chunk) -> GC::Ref { 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> 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 { 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()); diff --git a/Libraries/LibWeb/Encoding/TextEncoderStream.cpp b/Libraries/LibWeb/Encoding/TextEncoderStream.cpp index 6be87d9b93..3a904980bb 100644 --- a/Libraries/LibWeb/Encoding/TextEncoderStream.cpp +++ b/Libraries/LibWeb/Encoding/TextEncoderStream.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -38,6 +39,7 @@ GC::Ref TextEncoderStream::construct_impl(JS::Realm& realm) auto transform_algorithm = GC::create_function(realm.heap(), [stream](JS::Value chunk) -> GC::Ref { 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::construct_impl(JS::Realm& realm) auto flush_algorithm = GC::create_function(realm.heap(), [stream]() -> GC::Ref { 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()); diff --git a/Tests/LibWeb/Text/expected/wpt-import/encoding/streams/realms.window.txt b/Tests/LibWeb/Text/expected/wpt-import/encoding/streams/realms.window.txt index 173bb81dbe..2ca70d29cc 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/encoding/streams/realms.window.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/encoding/streams/realms.window.txt @@ -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 \ No newline at end of file +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