From f37d00c07b313c8a6821b8add97d803dbd049f58 Mon Sep 17 00:00:00 2001 From: Linus Groh Date: Sun, 12 Dec 2021 18:05:11 +0000 Subject: [PATCH] LibWeb: Implement TextEncoder.prototype.encode() --- .../LibWeb/WrapperGenerator.cpp | 2 +- Userland/Libraries/LibWeb/CMakeLists.txt | 1 + .../Libraries/LibWeb/Encoding/TextEncoder.cpp | 36 +++++++++++++++++++ .../Libraries/LibWeb/Encoding/TextEncoder.h | 2 ++ .../Libraries/LibWeb/Encoding/TextEncoder.idl | 2 +- .../Encoding/TextEncoder.prototype.encode.js | 28 +++++++++++++++ 6 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 Userland/Libraries/LibWeb/Encoding/TextEncoder.cpp create mode 100644 Userland/Libraries/LibWeb/Tests/Encoding/TextEncoder.prototype.encode.js diff --git a/Meta/Lagom/Tools/CodeGenerators/LibWeb/WrapperGenerator.cpp b/Meta/Lagom/Tools/CodeGenerators/LibWeb/WrapperGenerator.cpp index 916ce15a34..683c9c5d8b 100644 --- a/Meta/Lagom/Tools/CodeGenerators/LibWeb/WrapperGenerator.cpp +++ b/Meta/Lagom/Tools/CodeGenerators/LibWeb/WrapperGenerator.cpp @@ -1330,7 +1330,7 @@ static void generate_wrap_statement(SourceGenerator& generator, String const& va scoped_generator.append(R"~~~( @result_expression@ JS::Value((i32)@value@); )~~~"); - } else if (type.name == "Location" || type.name == "Promise" || type.name == "Uint8ClampedArray" || type.name == "any") { + } else if (type.name == "Location" || type.name == "Promise" || type.name == "Uint8Array" || type.name == "Uint8ClampedArray" || type.name == "any") { scoped_generator.append(R"~~~( @result_expression@ @value@; )~~~"); diff --git a/Userland/Libraries/LibWeb/CMakeLists.txt b/Userland/Libraries/LibWeb/CMakeLists.txt index cd37ce8d66..fd7b198de6 100644 --- a/Userland/Libraries/LibWeb/CMakeLists.txt +++ b/Userland/Libraries/LibWeb/CMakeLists.txt @@ -92,6 +92,7 @@ set(SOURCES DOMParsing/InnerHTML.cpp DOMTreeModel.cpp Dump.cpp + Encoding/TextEncoder.cpp FontCache.cpp HTML/AttributeNames.cpp HTML/BrowsingContext.cpp diff --git a/Userland/Libraries/LibWeb/Encoding/TextEncoder.cpp b/Userland/Libraries/LibWeb/Encoding/TextEncoder.cpp new file mode 100644 index 0000000000..a3a92231be --- /dev/null +++ b/Userland/Libraries/LibWeb/Encoding/TextEncoder.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2021, Linus Groh + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include + +namespace Web::Encoding { + +// https://encoding.spec.whatwg.org/#dom-textencoder-encode +JS::Uint8Array* TextEncoder::encode(String const& input) const +{ + auto& global_object = wrapper()->global_object(); + + // NOTE: The AK::String returned from PrimitiveString::string() is always UTF-8, regardless of the internal string type, so most of these steps are no-ops. + + // 1. Convert input to an I/O queue of scalar values. + // 2. Let output be the I/O queue of bytes « end-of-queue ». + // 3. While true: + // 1. Let item be the result of reading from input. + // 2. Let result be the result of processing an item with item, an instance of the UTF-8 encoder, input, output, and "fatal". + // 3. Assert: result is not an error. + // 4. If result is finished, then convert output into a byte sequence and return a Uint8Array object wrapping an ArrayBuffer containing output. + + auto byte_buffer = input.to_byte_buffer(); + + // FIXME: Support `TypedArray::create()` with existing `ArrayBuffer`, so that we don't have to allocate two `ByteBuffer`s. + auto* typed_array = JS::Uint8Array::create(global_object, byte_buffer.size()); + typed_array->viewed_array_buffer()->buffer() = move(byte_buffer); + return typed_array; +} + +} diff --git a/Userland/Libraries/LibWeb/Encoding/TextEncoder.h b/Userland/Libraries/LibWeb/Encoding/TextEncoder.h index b30d9264ce..2f97d536ba 100644 --- a/Userland/Libraries/LibWeb/Encoding/TextEncoder.h +++ b/Userland/Libraries/LibWeb/Encoding/TextEncoder.h @@ -32,6 +32,8 @@ public: return TextEncoder::create(); } + JS::Uint8Array* encode(String const& input) const; + protected: // https://encoding.spec.whatwg.org/#dom-textencoder TextEncoder() = default; diff --git a/Userland/Libraries/LibWeb/Encoding/TextEncoder.idl b/Userland/Libraries/LibWeb/Encoding/TextEncoder.idl index 09713478b7..8463e8a105 100644 --- a/Userland/Libraries/LibWeb/Encoding/TextEncoder.idl +++ b/Userland/Libraries/LibWeb/Encoding/TextEncoder.idl @@ -2,7 +2,7 @@ interface TextEncoder { constructor(); - // [NewObject] Uint8Array encode(optional USVString input = ""); + [NewObject] Uint8Array encode(optional USVString input = ""); // TextEncoderEncodeIntoResult encodeInto(USVString source, [AllowShared] Uint8Array destination); // readonly attribute DOMString encoding; diff --git a/Userland/Libraries/LibWeb/Tests/Encoding/TextEncoder.prototype.encode.js b/Userland/Libraries/LibWeb/Tests/Encoding/TextEncoder.prototype.encode.js new file mode 100644 index 0000000000..a8c8fdd126 --- /dev/null +++ b/Userland/Libraries/LibWeb/Tests/Encoding/TextEncoder.prototype.encode.js @@ -0,0 +1,28 @@ +describe("normal behavior", () => { + loadLocalPage("/res/html/misc/blank.html"); + + afterInitialPageLoad(page => { + test("Basic functionality", () => { + const textEncoder = new page.TextEncoder(); + + { + const typedArray = textEncoder.encode(""); + expect(typedArray).toHaveLength(0); + } + + { + const typedArray = textEncoder.encode("abc"); + expect(typedArray).toHaveLength(3); + expect(Array.from(typedArray)).toEqual([97, 98, 99]); + } + + { + const typedArray = textEncoder.encode("€"); + expect(typedArray).toHaveLength(3); + expect(Array.from(typedArray)).toEqual([226, 130, 172]); + // [255, 254, 172, 32] in UTF-16, but TextEncoder always converts JS UTF-16 strings to UTF-8 + } + }); + }); + waitForPageToLoad(); +});