Преглед на файлове

LibWeb: Make btoa() and atob() correctly handle values between 128 and 255

btoa() takes a byte string, so it must decode the UTF-8 argument into
a Vector<u8> before calling encode_base64.

Likewise, in atob() decode_base64 returns a byte string, so that needs
to be converted to UTF-8.

With this, `btoa(String.fromCharCode(255))` is '/w==' as it should
be, and `atob(btoa(String.fromCharCode(255))) == String.fromCharCode(255)`
remains true.
Nico Weber преди 5 години
родител
ревизия
b9ce56aee6
променени са 3 файла, в които са добавени 17 реда и са изтрити 4 реда
  1. 1 0
      Libraries/LibJS/Forward.h
  2. 1 0
      Libraries/LibJS/Runtime/ErrorTypes.h
  3. 15 4
      Libraries/LibWeb/Bindings/WindowObject.cpp

+ 1 - 0
Libraries/LibJS/Forward.h

@@ -61,6 +61,7 @@
 #define JS_ENUMERATE_ERROR_SUBCLASSES                                                                   \
     __JS_ENUMERATE(EvalError, eval_error, EvalErrorPrototype, EvalErrorConstructor)                     \
     __JS_ENUMERATE(InternalError, internal_error, InternalErrorPrototype, InternalErrorConstructor)     \
+    __JS_ENUMERATE(InvalidCharacterError, invalid_character_error, InvalidCharacterErrorPrototype, InvalidCharacterErrorConstructor)     \
     __JS_ENUMERATE(RangeError, range_error, RangeErrorPrototype, RangeErrorConstructor)                 \
     __JS_ENUMERATE(ReferenceError, reference_error, ReferenceErrorPrototype, ReferenceErrorConstructor) \
     __JS_ENUMERATE(SyntaxError, syntax_error, SyntaxErrorPrototype, SyntaxErrorConstructor)             \

+ 1 - 0
Libraries/LibJS/Runtime/ErrorTypes.h

@@ -152,6 +152,7 @@
     M(ToObjectNullOrUndef, "ToObject on null or undefined")                                            \
     M(UnknownIdentifier, "'%s' is not defined")                                                        \
     /* LibWeb bindings */                                                                              \
+    M(NotAByteString, "Argument to %s() must be a byte string")                                        \
     M(BadArgCountOne, "%s() needs one argument")                                                       \
     M(BadArgCountAtLeastOne, "%s() needs at least one argument")                                       \
     M(BadArgCountMany, "%s() needs %s arguments")

+ 15 - 4
Libraries/LibWeb/Bindings/WindowObject.cpp

@@ -28,11 +28,13 @@
 #include <AK/ByteBuffer.h>
 #include <AK/FlyString.h>
 #include <AK/Function.h>
+#include <AK/Utf8View.h>
 #include <AK/String.h>
 #include <LibJS/Interpreter.h>
 #include <LibJS/Runtime/Error.h>
 #include <LibJS/Runtime/Function.h>
 #include <LibJS/Runtime/Shape.h>
+#include <LibTextCodec/Decoder.h>
 #include <LibWeb/Bindings/DocumentWrapper.h>
 #include <LibWeb/Bindings/LocationObject.h>
 #include <LibWeb/Bindings/NavigatorObject.h>
@@ -251,9 +253,10 @@ JS_DEFINE_NATIVE_FUNCTION(WindowObject::atob)
     auto string = interpreter.argument(0).to_string(interpreter);
     if (interpreter.exception())
         return {};
-    // FIXME: This should convert string from a byte string to LibJS's internal string encoding (UTF-8).
     auto decoded = decode_base64(StringView(string));
-    return JS::js_string(interpreter, String::copy(decoded));
+
+    // decode_base64() returns a byte string. LibJS uses UTF-8 for strings. Use Latin1Decoder to convert bytes 128-255 to UTF-8.
+    return JS::js_string(interpreter, TextCodec::decoder_for("iso-8859-1")->to_utf8(decoded));
 }
 
 JS_DEFINE_NATIVE_FUNCTION(WindowObject::btoa)
@@ -266,8 +269,16 @@ JS_DEFINE_NATIVE_FUNCTION(WindowObject::btoa)
     auto string = interpreter.argument(0).to_string(interpreter);
     if (interpreter.exception())
         return {};
-    // FIXME: This should convert string to a non-UTF-8 byte string first.
-    auto encoded = encode_base64(ByteBuffer::wrap(string.characters(), string.length()));
+
+    Vector<u8> byte_string;
+    byte_string.ensure_capacity(string.length());
+    for (u32 codepoint : Utf8View(string)) {
+        if (codepoint > 0xff)
+            return interpreter.throw_exception<JS::InvalidCharacterError>(JS::ErrorType::NotAByteString, "btoa");
+        byte_string.append(codepoint);
+    }
+
+    auto encoded = encode_base64(ByteBuffer::wrap(byte_string.data(), byte_string.size()));
     return JS::js_string(interpreter, move(encoded));
 }