mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-01-06 11:09:05 +03:00
LibWeb: Add initial implementation of structured clone
This implementation only works for cloning Numbers, and does not try to do all the spec steps for structured serialize and deserialize. Co-Authored-By: Andrew Kaster <akaster@serenityos.org>
This commit is contained in:
parent
d94d60219c
commit
09841f56ed
Notes:
sideshowbarker
2024-07-19 17:02:01 +09:00
Author: https://github.com/ADKaster 🔰 Commit: https://github.com/SerenityOS/serenity/commit/09841f56ed2 Pull-request: https://github.com/SerenityOS/serenity/pull/16034
@ -281,6 +281,7 @@ set(SOURCES
|
||||
HTML/Scripting/Script.cpp
|
||||
HTML/Scripting/WindowEnvironmentSettingsObject.cpp
|
||||
HTML/Storage.cpp
|
||||
HTML/StructuredSerialize.cpp
|
||||
HTML/SubmitEvent.cpp
|
||||
HTML/SyntaxHighlighter/SyntaxHighlighter.cpp
|
||||
HTML/TagNames.cpp
|
||||
|
159
Userland/Libraries/LibWeb/HTML/StructuredSerialize.cpp
Normal file
159
Userland/Libraries/LibWeb/HTML/StructuredSerialize.cpp
Normal file
@ -0,0 +1,159 @@
|
||||
/*
|
||||
* Copyright (c) 2022, Daniel Ehrenberg <dan@littledan.dev>
|
||||
* Copyright (c) 2022, Andrew Kaster <akaster@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/HashTable.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibJS/Forward.h>
|
||||
#include <LibWeb/HTML/StructuredSerialize.h>
|
||||
#include <LibWeb/WebIDL/ExceptionOr.h>
|
||||
|
||||
namespace Web::HTML {
|
||||
|
||||
// Binary format:
|
||||
// A list of adjacent shallow values, which may contain references to other
|
||||
// values (noted by their position in the list, one value following another).
|
||||
// This list represents the "memory" in the StructuredSerialize algorithm.
|
||||
// The first item in the list is the root, i.e., the value of everything.
|
||||
// The format is generally u32-aligned (hence this leaking out into the type)
|
||||
// Each value has a length based on its type, as defined below.
|
||||
//
|
||||
// (Should more redundancy be added, e.g., for lengths/positions of values?)
|
||||
|
||||
enum ValueTag {
|
||||
// Unused, for ease of catching bugs
|
||||
Empty,
|
||||
|
||||
// Following two u32s are the double value
|
||||
NumberPrimitive,
|
||||
|
||||
// TODO: Define many more types
|
||||
|
||||
// This tag or higher are understood to be errors
|
||||
ValueTagMax,
|
||||
};
|
||||
|
||||
// Serializing and deserializing are each two passes:
|
||||
// 1. Fill up the memory with all the values, but without translating references
|
||||
// 2. Translate all the references into the appropriate form
|
||||
|
||||
class Serializer {
|
||||
public:
|
||||
Serializer(JS::VM& vm)
|
||||
: m_vm(vm)
|
||||
{
|
||||
}
|
||||
|
||||
void serialize(JS::Value value)
|
||||
{
|
||||
if (value.is_number()) {
|
||||
m_serialized.append(ValueTag::NumberPrimitive);
|
||||
double number = value.as_double();
|
||||
m_serialized.append(bit_cast<u32*>(&number), 2);
|
||||
} else {
|
||||
// TODO: Define many more types
|
||||
m_error = "Unsupported type"sv;
|
||||
}
|
||||
}
|
||||
|
||||
WebIDL::ExceptionOr<Vector<u32>> result()
|
||||
{
|
||||
if (m_error.is_null())
|
||||
return m_serialized;
|
||||
return throw_completion(WebIDL::DataCloneError::create(*m_vm.current_realm(), m_error));
|
||||
}
|
||||
|
||||
private:
|
||||
AK::StringView m_error;
|
||||
SerializationMemory m_memory; // JS value -> index
|
||||
SerializationRecord m_serialized;
|
||||
JS::VM& m_vm;
|
||||
};
|
||||
|
||||
class Deserializer {
|
||||
public:
|
||||
Deserializer(JS::VM& vm, JS::Realm& target_realm, SerializationRecord const& v)
|
||||
: m_vm(vm)
|
||||
, m_vector(v)
|
||||
, m_memory(target_realm.heap())
|
||||
{
|
||||
}
|
||||
|
||||
void deserialize()
|
||||
{
|
||||
// First pass: fill up the memory with new values
|
||||
u32 position = 0;
|
||||
while (position < m_vector.size()) {
|
||||
switch (m_vector[position++]) {
|
||||
case ValueTag::NumberPrimitive: {
|
||||
u32 bits[2];
|
||||
bits[0] = m_vector[position++];
|
||||
bits[1] = m_vector[position++];
|
||||
double value = *bit_cast<double*>(&bits);
|
||||
m_memory.append(JS::Value(value));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
m_error = "Unsupported type"sv;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Second pass: Update the objects to point to other objects in memory
|
||||
}
|
||||
|
||||
WebIDL::ExceptionOr<JS::Value> result()
|
||||
{
|
||||
if (m_error.is_null())
|
||||
return m_memory[0];
|
||||
return throw_completion(WebIDL::DataCloneError::create(*m_vm.current_realm(), m_error));
|
||||
}
|
||||
|
||||
private:
|
||||
JS::VM& m_vm;
|
||||
SerializationRecord const& m_vector;
|
||||
JS::MarkedVector<JS::Value> m_memory; // Index -> JS value
|
||||
StringView m_error;
|
||||
};
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/structured-data.html#structuredserialize
|
||||
WebIDL::ExceptionOr<SerializationRecord> structured_serialize(JS::VM& vm, JS::Value value)
|
||||
{
|
||||
// 1. Return ? StructuredSerializeInternal(value, false).
|
||||
return structured_serialize_internal(vm, value, false, {});
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/structured-data.html#structuredserializeforstorage
|
||||
WebIDL::ExceptionOr<SerializationRecord> structured_serialize_for_storage(JS::VM& vm, JS::Value value)
|
||||
{
|
||||
// 1. Return ? StructuredSerializeInternal(value, true).
|
||||
return structured_serialize_internal(vm, value, true, {});
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/structured-data.html#structuredserializeinternal
|
||||
WebIDL::ExceptionOr<SerializationRecord> structured_serialize_internal(JS::VM& vm, JS::Value value, bool for_storage, Optional<SerializationMemory> memory)
|
||||
{
|
||||
// FIXME: Do the spec steps
|
||||
(void)for_storage;
|
||||
(void)memory;
|
||||
|
||||
Serializer serializer(vm);
|
||||
serializer.serialize(value);
|
||||
return serializer.result(); // TODO: Avoid several copies of vector
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/structured-data.html#structureddeserialize
|
||||
WebIDL::ExceptionOr<JS::Value> structured_deserialize(JS::VM& vm, SerializationRecord const& serialized, JS::Realm& target_realm, Optional<SerializationMemory> memory)
|
||||
{
|
||||
// FIXME: Do the spec steps
|
||||
(void)memory;
|
||||
|
||||
Deserializer deserializer(vm, target_realm, serialized);
|
||||
deserializer.deserialize();
|
||||
return deserializer.result();
|
||||
}
|
||||
|
||||
}
|
34
Userland/Libraries/LibWeb/HTML/StructuredSerialize.h
Normal file
34
Userland/Libraries/LibWeb/HTML/StructuredSerialize.h
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (c) 2022, Daniel Ehrenberg <dan@littledan.dev>
|
||||
* Copyright (c) 2022, Andrew Kaster <akaster@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Result.h>
|
||||
#include <AK/Types.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibJS/Forward.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 {
|
||||
|
||||
using SerializationRecord = Vector<u32>;
|
||||
using SerializationMemory = HashMap<JS::Handle<JS::Value>, u32>;
|
||||
|
||||
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, Optional<SerializationMemory>);
|
||||
|
||||
WebIDL::ExceptionOr<JS::Value> structured_deserialize(JS::VM& vm, SerializationRecord const& serialized, JS::Realm& target_realm, Optional<SerializationMemory>);
|
||||
|
||||
// TODO: structured_[de]serialize_with_transfer
|
||||
|
||||
}
|
@ -44,6 +44,7 @@
|
||||
#include <LibWeb/HTML/Scripting/Environments.h>
|
||||
#include <LibWeb/HTML/Scripting/ExceptionReporter.h>
|
||||
#include <LibWeb/HTML/Storage.h>
|
||||
#include <LibWeb/HTML/StructuredSerialize.h>
|
||||
#include <LibWeb/HTML/Timer.h>
|
||||
#include <LibWeb/HTML/Window.h>
|
||||
#include <LibWeb/HTML/WindowProxy.h>
|
||||
@ -915,6 +916,13 @@ WebIDL::ExceptionOr<void> Window::post_message_impl(JS::Value message, String co
|
||||
return {};
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/structured-data.html#dom-structuredclone
|
||||
WebIDL::ExceptionOr<JS::Value> Window::structured_clone_impl(JS::VM& vm, JS::Value message)
|
||||
{
|
||||
auto serialized = TRY(structured_serialize(vm, message));
|
||||
return MUST(structured_deserialize(vm, serialized, *vm.current_realm(), {}));
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/window-object.html#dom-name
|
||||
String Window::name() const
|
||||
{
|
||||
@ -1100,6 +1108,7 @@ void Window::initialize_web_interfaces(Badge<WindowEnvironmentSettingsObject>)
|
||||
define_native_function(realm, "getSelection", get_selection, 0, attr);
|
||||
|
||||
define_native_function(realm, "postMessage", post_message, 1, attr);
|
||||
define_native_function(realm, "structuredClone", structured_clone, 1, attr);
|
||||
|
||||
define_native_function(realm, "fetch", Bindings::fetch, 1, attr);
|
||||
|
||||
@ -1824,6 +1833,14 @@ JS_DEFINE_NATIVE_FUNCTION(Window::post_message)
|
||||
return JS::js_undefined();
|
||||
}
|
||||
|
||||
JS_DEFINE_NATIVE_FUNCTION(Window::structured_clone)
|
||||
{
|
||||
auto* impl = TRY(impl_from(vm));
|
||||
return TRY(Bindings::throw_dom_exception_if_needed(vm, [&] {
|
||||
return impl->structured_clone_impl(vm, vm.argument(0));
|
||||
}));
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/webappapis.html#dom-origin
|
||||
JS_DEFINE_NATIVE_FUNCTION(Window::origin_getter)
|
||||
{
|
||||
|
@ -119,6 +119,7 @@ public:
|
||||
WindowProxy* parent();
|
||||
|
||||
WebIDL::ExceptionOr<void> post_message_impl(JS::Value, String const& target_origin);
|
||||
WebIDL::ExceptionOr<JS::Value> structured_clone_impl(JS::VM& vm, JS::Value);
|
||||
|
||||
String name() const;
|
||||
void set_name(String const&);
|
||||
@ -241,6 +242,7 @@ private:
|
||||
JS_DECLARE_NATIVE_FUNCTION(outer_height_getter);
|
||||
|
||||
JS_DECLARE_NATIVE_FUNCTION(post_message);
|
||||
JS_DECLARE_NATIVE_FUNCTION(structured_clone);
|
||||
|
||||
JS_DECLARE_NATIVE_FUNCTION(local_storage_getter);
|
||||
JS_DECLARE_NATIVE_FUNCTION(session_storage_getter);
|
||||
|
Loading…
Reference in New Issue
Block a user