mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-07-23 17:33:12 +00:00
Our currently implementation of structured serialization has a design flaw, where if the serialized/transferred type was not used in the destination realm, it would not be seen as exposed and thus we would not re-create the type on the other side. This is very common, for example, transferring a MessagePort to a just inserted iframe, or the just inserted iframe transferring a MessagePort to it's parent. This is what Google reCAPTCHA does. This flaw occurred due to relying on lazily populated HashMaps of constructors, namespaces and interfaces. This commit changes it so that per-type "is exposed" implementations are generated. Since it no longer relies on interface name strings, this commit changes serializable types to indicate their type with an enum, in line with how transferrable types indicate their type. This makes Google reCAPTCHA work on https://www.google.com/recaptcha/api2/demo It currently doesn't work on non-Google origins due to a separate same-origin policy bug.
138 lines
6.6 KiB
C++
138 lines
6.6 KiB
C++
/*
|
|
* Copyright (c) 2022, Daniel Ehrenberg <dan@littledan.dev>
|
|
* Copyright (c) 2022, Andrew Kaster <akaster@serenityos.org>
|
|
* Copyright (c) 2024, Kenneth Myhra <kennethmyhra@serenityos.org>
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <AK/Result.h>
|
|
#include <AK/Types.h>
|
|
#include <AK/Vector.h>
|
|
#include <LibIPC/Forward.h>
|
|
#include <LibJS/Forward.h>
|
|
#include <LibWeb/Forward.h>
|
|
#include <LibWeb/HTML/StructuredSerializeTypes.h>
|
|
#include <LibWeb/WebIDL/ExceptionOr.h>
|
|
|
|
// Structured serialize is an entirely different format from IPC because:
|
|
// - It contains representation of type information
|
|
// - It may contain circularities
|
|
// - It is restricted to JS values
|
|
|
|
namespace Web::HTML {
|
|
|
|
struct TransferDataHolder {
|
|
Vector<u32> data;
|
|
Vector<IPC::File> fds;
|
|
};
|
|
|
|
struct SerializedTransferRecord {
|
|
SerializationRecord serialized;
|
|
Vector<TransferDataHolder> transfer_data_holders;
|
|
};
|
|
|
|
struct DeserializedTransferRecord {
|
|
JS::Value deserialized;
|
|
Vector<GC::Root<JS::Object>> transferred_values;
|
|
};
|
|
|
|
struct DeserializedRecord {
|
|
Optional<JS::Value> value;
|
|
size_t position;
|
|
};
|
|
|
|
WebIDL::ExceptionOr<SerializationRecord> structured_serialize(JS::VM& vm, JS::Value);
|
|
WebIDL::ExceptionOr<SerializationRecord> structured_serialize_for_storage(JS::VM& vm, JS::Value);
|
|
WebIDL::ExceptionOr<SerializationRecord> structured_serialize_internal(JS::VM& vm, JS::Value, bool for_storage, SerializationMemory&);
|
|
|
|
WebIDL::ExceptionOr<JS::Value> structured_deserialize(JS::VM& vm, SerializationRecord const& serialized, JS::Realm& target_realm, Optional<DeserializationMemory> = {});
|
|
WebIDL::ExceptionOr<DeserializedRecord> structured_deserialize_internal(JS::VM& vm, ReadonlySpan<u32> const& serialized, JS::Realm& target_realm, DeserializationMemory& memory, Optional<size_t> position = {});
|
|
|
|
void serialize_boolean_primitive(SerializationRecord& serialized, JS::Value& value);
|
|
void serialize_number_primitive(SerializationRecord& serialized, JS::Value& value);
|
|
WebIDL::ExceptionOr<void> serialize_big_int_primitive(JS::VM& vm, SerializationRecord& serialized, JS::Value& value);
|
|
WebIDL::ExceptionOr<void> serialize_string_primitive(JS::VM& vm, SerializationRecord& serialized, JS::Value& value);
|
|
void serialize_boolean_object(SerializationRecord& serialized, JS::Value& value);
|
|
void serialize_number_object(SerializationRecord& serialized, JS::Value& value);
|
|
WebIDL::ExceptionOr<void> serialize_big_int_object(JS::VM& vm, SerializationRecord& serialized, JS::Value& value);
|
|
WebIDL::ExceptionOr<void> serialize_string_object(JS::VM& vm, SerializationRecord& serialized, JS::Value& value);
|
|
void serialize_date_object(SerializationRecord& serialized, JS::Value& value);
|
|
WebIDL::ExceptionOr<void> serialize_reg_exp_object(JS::VM& vm, SerializationRecord& serialized, JS::Value& value);
|
|
|
|
template<typename T>
|
|
requires(IsIntegral<T> || IsFloatingPoint<T>)
|
|
void serialize_primitive_type(SerializationRecord& serialized, T value)
|
|
{
|
|
if constexpr (sizeof(T) < sizeof(u32)) {
|
|
// NOTE: If the value is smaller than a u32, we can just store it directly.
|
|
serialized.append(static_cast<u32>(value));
|
|
return;
|
|
}
|
|
serialized.append(bit_cast<u32*>(&value), sizeof(T) / 4);
|
|
}
|
|
|
|
template<typename T>
|
|
requires(IsEnum<T>)
|
|
void serialize_enum(SerializationRecord& serialized, T value)
|
|
{
|
|
serialize_primitive_type<UnderlyingType<T>>(serialized, to_underlying(value));
|
|
}
|
|
|
|
WebIDL::ExceptionOr<void> serialize_bytes(JS::VM& vm, Vector<u32>& vector, ReadonlyBytes bytes);
|
|
WebIDL::ExceptionOr<void> serialize_string(JS::VM& vm, Vector<u32>& vector, StringView);
|
|
WebIDL::ExceptionOr<void> serialize_string(JS::VM& vm, Vector<u32>& vector, String const& string);
|
|
WebIDL::ExceptionOr<void> serialize_string(JS::VM& vm, Vector<u32>& vector, JS::PrimitiveString const& primitive_string);
|
|
WebIDL::ExceptionOr<void> serialize_array_buffer(JS::VM& vm, Vector<u32>& vector, JS::ArrayBuffer const& array_buffer, bool for_storage);
|
|
template<OneOf<JS::TypedArrayBase, JS::DataView> ViewType>
|
|
WebIDL::ExceptionOr<void> serialize_viewed_array_buffer(JS::VM& vm, Vector<u32>& vector, ViewType const& view, bool for_storage, SerializationMemory& memory);
|
|
|
|
bool deserialize_boolean_primitive(ReadonlySpan<u32> const& serialized, size_t& position);
|
|
double deserialize_number_primitive(ReadonlySpan<u32> const& serialized, size_t& position);
|
|
GC::Ref<JS::BooleanObject> deserialize_boolean_object(JS::Realm& realm, ReadonlySpan<u32> const& serialized, size_t& position);
|
|
GC::Ref<JS::NumberObject> deserialize_number_object(JS::Realm& realm, ReadonlySpan<u32> const& serialized, size_t& position);
|
|
WebIDL::ExceptionOr<GC::Ref<JS::BigIntObject>> deserialize_big_int_object(JS::Realm& realm, ReadonlySpan<u32> const& serialized, size_t& position);
|
|
WebIDL::ExceptionOr<GC::Ref<JS::StringObject>> deserialize_string_object(JS::Realm& realm, ReadonlySpan<u32> const& serialized, size_t& position);
|
|
GC::Ref<JS::Date> deserialize_date_object(JS::Realm& realm, ReadonlySpan<u32> const& serialized, size_t& position);
|
|
WebIDL::ExceptionOr<GC::Ref<JS::RegExpObject>> deserialize_reg_exp_object(JS::Realm& realm, ReadonlySpan<u32> const& serialized, size_t& position);
|
|
|
|
template<typename T>
|
|
requires(IsIntegral<T> || IsFloatingPoint<T> || IsEnum<T>)
|
|
T deserialize_primitive_type(ReadonlySpan<u32> const& serialized, size_t& position)
|
|
{
|
|
T value;
|
|
// NOTE: Make sure we always round up, otherwise Ts that are less than 32 bit will end up with a size of 0.
|
|
auto size = 1 + ((sizeof(value) - 1) / 4);
|
|
VERIFY(position + size <= serialized.size());
|
|
memcpy(&value, serialized.offset_pointer(position), sizeof(value));
|
|
position += size;
|
|
return value;
|
|
}
|
|
|
|
WebIDL::ExceptionOr<ByteBuffer> deserialize_bytes(JS::VM& vm, ReadonlySpan<u32> vector, size_t& position);
|
|
WebIDL::ExceptionOr<String> deserialize_string(JS::VM& vm, ReadonlySpan<u32> vector, size_t& position);
|
|
WebIDL::ExceptionOr<GC::Ref<JS::PrimitiveString>> deserialize_string_primitive(JS::VM& vm, ReadonlySpan<u32> vector, size_t& position);
|
|
WebIDL::ExceptionOr<GC::Ref<JS::BigInt>> deserialize_big_int_primitive(JS::VM& vm, ReadonlySpan<u32> vector, size_t& position);
|
|
|
|
WebIDL::ExceptionOr<SerializedTransferRecord> structured_serialize_with_transfer(JS::VM& vm, JS::Value value, Vector<GC::Root<JS::Object>> const& transfer_list);
|
|
WebIDL::ExceptionOr<DeserializedTransferRecord> structured_deserialize_with_transfer(SerializedTransferRecord&, JS::Realm& target_realm);
|
|
|
|
}
|
|
|
|
namespace IPC {
|
|
|
|
template<>
|
|
ErrorOr<void> encode(Encoder&, ::Web::HTML::SerializedTransferRecord const&);
|
|
|
|
template<>
|
|
ErrorOr<void> encode(Encoder&, ::Web::HTML::TransferDataHolder const&);
|
|
|
|
template<>
|
|
ErrorOr<::Web::HTML::SerializedTransferRecord> decode(Decoder&);
|
|
|
|
template<>
|
|
ErrorOr<::Web::HTML::TransferDataHolder> decode(Decoder&);
|
|
|
|
}
|