StructuredSerialize.cpp 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. /*
  2. * Copyright (c) 2022, Daniel Ehrenberg <dan@littledan.dev>
  3. * Copyright (c) 2022, Andrew Kaster <akaster@serenityos.org>
  4. * Copyright (c) 2023, Kenneth Myhra <kennethmyhra@serenityos.org>
  5. *
  6. * SPDX-License-Identifier: BSD-2-Clause
  7. */
  8. #include <AK/HashTable.h>
  9. #include <AK/String.h>
  10. #include <AK/Vector.h>
  11. #include <LibJS/Forward.h>
  12. #include <LibJS/Runtime/BigInt.h>
  13. #include <LibJS/Runtime/PrimitiveString.h>
  14. #include <LibJS/Runtime/VM.h>
  15. #include <LibWeb/Bindings/ExceptionOrUtils.h>
  16. #include <LibWeb/HTML/StructuredSerialize.h>
  17. #include <LibWeb/WebIDL/ExceptionOr.h>
  18. namespace Web::HTML {
  19. // Binary format:
  20. // A list of adjacent shallow values, which may contain references to other
  21. // values (noted by their position in the list, one value following another).
  22. // This list represents the "memory" in the StructuredSerialize algorithm.
  23. // The first item in the list is the root, i.e., the value of everything.
  24. // The format is generally u32-aligned (hence this leaking out into the type)
  25. // Each value has a length based on its type, as defined below.
  26. //
  27. // (Should more redundancy be added, e.g., for lengths/positions of values?)
  28. enum ValueTag {
  29. // Unused, for ease of catching bugs.
  30. Empty,
  31. // UndefinedPrimitive is serialized indicating that the Type is Undefined, no value is serialized.
  32. UndefinedPrimitive,
  33. // NullPrimitive is serialized indicating that the Type is Null, no value is serialized.
  34. NullPrimitive,
  35. // Following u32 is the boolean value.
  36. BooleanPrimitive,
  37. // Following two u32s are the double value.
  38. NumberPrimitive,
  39. // The BigIntPrimitive is serialized as a string in base 10 representation.
  40. // Following two u32s representing the length of the string, then the following u32s, equal to size, is the string representation.
  41. BigIntPrimitive,
  42. // Following two u32s representing the length of the string, then the following u32s, equal to size, is the string representation.
  43. StringPrimitive,
  44. // TODO: Define many more types
  45. // This tag or higher are understood to be errors
  46. ValueTagMax,
  47. };
  48. // Serializing and deserializing are each two passes:
  49. // 1. Fill up the memory with all the values, but without translating references
  50. // 2. Translate all the references into the appropriate form
  51. class Serializer {
  52. public:
  53. Serializer(JS::VM& vm)
  54. : m_vm(vm)
  55. {
  56. }
  57. WebIDL::ExceptionOr<void> serialize(JS::Value value)
  58. {
  59. if (value.is_undefined()) {
  60. m_serialized.append(ValueTag::UndefinedPrimitive);
  61. } else if (value.is_null()) {
  62. m_serialized.append(ValueTag::NullPrimitive);
  63. } else if (value.is_boolean()) {
  64. m_serialized.append(ValueTag::BooleanPrimitive);
  65. m_serialized.append(static_cast<u32>(value.as_bool()));
  66. } else if (value.is_number()) {
  67. m_serialized.append(ValueTag::NumberPrimitive);
  68. double number = value.as_double();
  69. m_serialized.append(bit_cast<u32*>(&number), 2);
  70. } else if (value.is_bigint()) {
  71. m_serialized.append(ValueTag::BigIntPrimitive);
  72. auto& val = value.as_bigint();
  73. TRY(serialize_string(m_serialized, TRY_OR_THROW_OOM(m_vm, val.to_string())));
  74. } else if (value.is_string()) {
  75. m_serialized.append(ValueTag::StringPrimitive);
  76. TRY(serialize_string(m_serialized, value.as_string()));
  77. } else {
  78. // TODO: Define many more types
  79. m_error = "Unsupported type"sv;
  80. }
  81. // Second pass: Update the objects to point to other objects in memory
  82. return {};
  83. }
  84. WebIDL::ExceptionOr<Vector<u32>> result()
  85. {
  86. if (m_error.is_null())
  87. return m_serialized;
  88. return throw_completion(WebIDL::DataCloneError::create(*m_vm.current_realm(), m_error));
  89. }
  90. private:
  91. AK::StringView m_error;
  92. SerializationMemory m_memory; // JS value -> index
  93. SerializationRecord m_serialized;
  94. JS::VM& m_vm;
  95. WebIDL::ExceptionOr<void> serialize_string(Vector<u32>& vector, String const& string)
  96. {
  97. u64 const size = string.code_points().length();
  98. // Append size of the string to the serialized structure.
  99. TRY_OR_THROW_OOM(m_vm, vector.try_append(bit_cast<u32*>(&size), 2));
  100. for (auto code_point : string.code_points()) {
  101. // Append each code point to the serialized structure.
  102. TRY_OR_THROW_OOM(m_vm, vector.try_append(code_point));
  103. }
  104. return {};
  105. }
  106. WebIDL::ExceptionOr<void> serialize_string(Vector<u32>& vector, JS::PrimitiveString const& primitive_string)
  107. {
  108. auto string = TRY(Bindings::throw_dom_exception_if_needed(m_vm, [&primitive_string]() {
  109. return primitive_string.utf8_string();
  110. }));
  111. TRY(serialize_string(vector, string));
  112. return {};
  113. }
  114. };
  115. class Deserializer {
  116. public:
  117. Deserializer(JS::VM& vm, JS::Realm& target_realm, SerializationRecord const& v)
  118. : m_vm(vm)
  119. , m_vector(v)
  120. , m_memory(target_realm.heap())
  121. {
  122. }
  123. WebIDL::ExceptionOr<void> deserialize()
  124. {
  125. // First pass: fill up the memory with new values
  126. u32 position = 0;
  127. while (position < m_vector.size()) {
  128. switch (m_vector[position++]) {
  129. case ValueTag::UndefinedPrimitive: {
  130. m_memory.append(JS::js_undefined());
  131. break;
  132. }
  133. case ValueTag::NullPrimitive: {
  134. m_memory.append(JS::js_null());
  135. break;
  136. }
  137. case ValueTag::BooleanPrimitive: {
  138. m_memory.append(JS::Value(static_cast<bool>(m_vector[position++])));
  139. break;
  140. }
  141. case ValueTag::NumberPrimitive: {
  142. u32 bits[2];
  143. bits[0] = m_vector[position++];
  144. bits[1] = m_vector[position++];
  145. double value = *bit_cast<double*>(&bits);
  146. m_memory.append(JS::Value(value));
  147. break;
  148. }
  149. case ValueTag::BigIntPrimitive: {
  150. auto big_int = TRY(deserialize_big_int_primitive(m_vm, m_vector, position));
  151. m_memory.append(JS::Value { big_int });
  152. break;
  153. }
  154. case ValueTag::StringPrimitive: {
  155. auto string = TRY(deserialize_string_primitive(m_vm, m_vector, position));
  156. m_memory.append(JS::Value { string });
  157. break;
  158. }
  159. default:
  160. m_error = "Unsupported type"sv;
  161. break;
  162. }
  163. }
  164. return {};
  165. }
  166. WebIDL::ExceptionOr<JS::Value> result()
  167. {
  168. if (m_error.is_null())
  169. return m_memory[0];
  170. return throw_completion(WebIDL::DataCloneError::create(*m_vm.current_realm(), m_error));
  171. }
  172. private:
  173. JS::VM& m_vm;
  174. SerializationRecord const& m_vector;
  175. JS::MarkedVector<JS::Value> m_memory; // Index -> JS value
  176. StringView m_error;
  177. static WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::PrimitiveString>> deserialize_string_primitive(JS::VM& vm, Vector<u32> const& vector, u32& position)
  178. {
  179. u32 size_bits[2];
  180. size_bits[0] = vector[position++];
  181. size_bits[1] = vector[position++];
  182. u64 const size = *bit_cast<u64*>(&size_bits);
  183. u8 bits[size];
  184. for (u32 i = 0; i < size; ++i)
  185. bits[i] = vector[position++];
  186. ReadonlyBytes const bytes = { bits, size };
  187. return TRY(Bindings::throw_dom_exception_if_needed(vm, [&vm, &bytes]() {
  188. return JS::PrimitiveString::create(vm, StringView { bytes });
  189. }));
  190. }
  191. static WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::BigInt>> deserialize_big_int_primitive(JS::VM& vm, Vector<u32> const& vector, u32& position)
  192. {
  193. auto string = TRY(deserialize_string_primitive(vm, vector, position));
  194. auto string_view = TRY(Bindings::throw_dom_exception_if_needed(vm, [&string]() {
  195. return string->utf8_string_view();
  196. }));
  197. return JS::BigInt::create(vm, ::Crypto::SignedBigInteger::from_base(10, string_view.substring_view(0, string_view.length() - 1)));
  198. }
  199. };
  200. // https://html.spec.whatwg.org/multipage/structured-data.html#structuredserialize
  201. WebIDL::ExceptionOr<SerializationRecord> structured_serialize(JS::VM& vm, JS::Value value)
  202. {
  203. // 1. Return ? StructuredSerializeInternal(value, false).
  204. return structured_serialize_internal(vm, value, false, {});
  205. }
  206. // https://html.spec.whatwg.org/multipage/structured-data.html#structuredserializeforstorage
  207. WebIDL::ExceptionOr<SerializationRecord> structured_serialize_for_storage(JS::VM& vm, JS::Value value)
  208. {
  209. // 1. Return ? StructuredSerializeInternal(value, true).
  210. return structured_serialize_internal(vm, value, true, {});
  211. }
  212. // https://html.spec.whatwg.org/multipage/structured-data.html#structuredserializeinternal
  213. WebIDL::ExceptionOr<SerializationRecord> structured_serialize_internal(JS::VM& vm, JS::Value value, bool for_storage, Optional<SerializationMemory> memory)
  214. {
  215. // FIXME: Do the spec steps
  216. (void)for_storage;
  217. (void)memory;
  218. Serializer serializer(vm);
  219. TRY(serializer.serialize(value));
  220. return serializer.result(); // TODO: Avoid several copies of vector
  221. }
  222. // https://html.spec.whatwg.org/multipage/structured-data.html#structureddeserialize
  223. WebIDL::ExceptionOr<JS::Value> structured_deserialize(JS::VM& vm, SerializationRecord const& serialized, JS::Realm& target_realm, Optional<SerializationMemory> memory)
  224. {
  225. // FIXME: Do the spec steps
  226. (void)memory;
  227. Deserializer deserializer(vm, target_realm, serialized);
  228. TRY(deserializer.deserialize());
  229. return deserializer.result();
  230. }
  231. }