mirror of
https://github.com/lexmount/moli.git
synced 2026-10-07 08:01:00 +00:00
fix(indexeddb): clone ImageData and geometry objects
This commit is contained in:
@@ -2,16 +2,19 @@ use super::*;
|
||||
use crate::{
|
||||
context_bootstrap::{
|
||||
FileSystemHandleDurablePayload, build_file_list_object,
|
||||
build_file_system_handle_from_durable_payload, file_list_files_from_object,
|
||||
file_system_handle_clone_payload_from_object,
|
||||
file_system_handle_durable_payload_from_object, is_file_list_object,
|
||||
build_file_system_handle_from_durable_payload, build_geometry_object_from_clone_payload,
|
||||
file_list_files_from_object, file_system_handle_clone_payload_from_object,
|
||||
file_system_handle_durable_payload_from_object, geometry_clone_payload_from_object,
|
||||
image_data_clone_payload_from_object, is_file_list_object,
|
||||
},
|
||||
dom::native::SelectedFile,
|
||||
structured_clone::{
|
||||
BlobClonePayload, HOST_OBJECT_TAG_BLOB, HOST_OBJECT_TAG_CRYPTO_KEY,
|
||||
HOST_OBJECT_TAG_FILE_LIST, HOST_OBJECT_TAG_FILE_SYSTEM_HANDLE,
|
||||
blob_clone_payload_from_object, build_blob_object_from_clone_payload,
|
||||
read_crypto_key_payload, write_crypto_key_payload,
|
||||
HOST_OBJECT_TAG_FILE_LIST, HOST_OBJECT_TAG_FILE_SYSTEM_HANDLE, HOST_OBJECT_TAG_GEOMETRY,
|
||||
HOST_OBJECT_TAG_IMAGE_DATA, blob_clone_payload_from_object,
|
||||
build_blob_object_from_clone_payload, read_crypto_key_payload, read_geometry_clone_payload,
|
||||
read_image_data_payload, write_crypto_key_payload, write_geometry_clone_payload,
|
||||
write_image_data_payload,
|
||||
},
|
||||
};
|
||||
use moli_indexeddb::{IndexedDbFileSystemHandleBucket, IndexedDbFileSystemHandleKind};
|
||||
@@ -88,6 +91,8 @@ impl v8::ValueSerializerImpl for IndexedDbStructuredCloneSerializer {
|
||||
) -> Option<bool> {
|
||||
Some(
|
||||
crate::context_bootstrap::is_crypto_key_object(scope, object)
|
||||
|| image_data_clone_payload_from_object(scope, object).is_some()
|
||||
|| geometry_clone_payload_from_object(scope, object).is_some()
|
||||
|| crate::blob::is_blob_object(scope, object)
|
||||
|| is_file_list_object(scope, object)
|
||||
|| file_system_handle_clone_payload_from_object(scope, object).is_some()
|
||||
@@ -104,6 +109,14 @@ impl v8::ValueSerializerImpl for IndexedDbStructuredCloneSerializer {
|
||||
if write_crypto_key_payload(scope, object, serializer).is_some() {
|
||||
return Some(true);
|
||||
}
|
||||
if let Some(payload) = image_data_clone_payload_from_object(scope, object) {
|
||||
write_image_data_payload(serializer, payload);
|
||||
return Some(true);
|
||||
}
|
||||
if let Some(payload) = geometry_clone_payload_from_object(scope, object) {
|
||||
write_geometry_clone_payload(serializer, payload);
|
||||
return Some(true);
|
||||
}
|
||||
if is_file_list_object(scope, object) {
|
||||
let Some(files) = file_list_files_from_object(scope, object) else {
|
||||
let exception = dom_exception_value(
|
||||
@@ -245,6 +258,7 @@ impl v8::ValueDeserializerImpl for IndexedDbStructuredCloneDeserializer {
|
||||
return None;
|
||||
}
|
||||
match tag {
|
||||
HOST_OBJECT_TAG_IMAGE_DATA => read_image_data_payload(scope, deserializer),
|
||||
HOST_OBJECT_TAG_CRYPTO_KEY => {
|
||||
read_crypto_key_payload(scope, deserializer).or_else(|| {
|
||||
let exception = dom_exception_value(
|
||||
@@ -256,6 +270,17 @@ impl v8::ValueDeserializerImpl for IndexedDbStructuredCloneDeserializer {
|
||||
None
|
||||
})
|
||||
}
|
||||
HOST_OBJECT_TAG_GEOMETRY => read_geometry_clone_payload(deserializer)
|
||||
.map(|payload| build_geometry_object_from_clone_payload(scope, payload))
|
||||
.or_else(|| {
|
||||
let exception = dom_exception_value(
|
||||
scope,
|
||||
"Failed to deserialize IndexedDB Geometry object.",
|
||||
"DataCloneError",
|
||||
);
|
||||
scope.throw_exception(exception);
|
||||
None
|
||||
}),
|
||||
HOST_OBJECT_TAG_BLOB => {
|
||||
let mut index = 0;
|
||||
if !deserializer.read_uint32(&mut index) {
|
||||
|
||||
@@ -1263,6 +1263,152 @@ fn indexed_db_roundtrips_file_list_with_file_graph_identity() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn indexed_db_roundtrips_image_data_and_geometry_objects() {
|
||||
let mut vm =
|
||||
new_storage_page_task_executor_test_vm("https://indexeddb-image-data-geometry.test/");
|
||||
|
||||
vm.eval(
|
||||
r#"
|
||||
(() => {
|
||||
globalThis.__indexedDbGeometryResult = "pending";
|
||||
const dbName = `image-data-geometry-${Math.random()}`;
|
||||
const open = indexedDB.open(dbName, 1);
|
||||
open.onerror = () => {
|
||||
globalThis.__indexedDbGeometryResult = `open-error:${open.error && open.error.name}`;
|
||||
};
|
||||
open.onupgradeneeded = () => open.result.createObjectStore("values");
|
||||
open.onsuccess = () => {
|
||||
const image = new ImageData(
|
||||
new Uint8ClampedArray([1, 2, 3, 4, 5, 6, 7, 8]),
|
||||
2,
|
||||
1,
|
||||
{ colorSpace: "display-p3" }
|
||||
);
|
||||
const pointReadOnly = new DOMPointReadOnly(1, -0, Infinity, NaN);
|
||||
const point = new DOMPoint(2, 3, 4, 5);
|
||||
const rectReadOnly = new DOMRectReadOnly(6, 7, 8, 9);
|
||||
const rect = new DOMRect(10, 11, 12, 13);
|
||||
const quad = new DOMQuad(
|
||||
{ x: 1, y: 2, z: 3, w: 4 },
|
||||
{ x: 5, y: 6, z: 7, w: 8 },
|
||||
{ x: 9, y: 10, z: 11, w: 12 },
|
||||
{ x: 13, y: 14, z: 15, w: 16 }
|
||||
);
|
||||
const matrixReadOnly = new DOMMatrixReadOnly([1, 2, 3, 4, 5, 6]);
|
||||
const matrix = new DOMMatrix([
|
||||
11, 12, 13, 14,
|
||||
21, 22, 23, 24,
|
||||
31, 32, 33, 34,
|
||||
41, 42, 43, 44
|
||||
]);
|
||||
for (const value of [
|
||||
image,
|
||||
pointReadOnly,
|
||||
point,
|
||||
rectReadOnly,
|
||||
rect,
|
||||
quad,
|
||||
matrixReadOnly,
|
||||
matrix
|
||||
]) {
|
||||
value.expando = "not serialized";
|
||||
}
|
||||
|
||||
const write = open.result.transaction("values", "readwrite");
|
||||
write.objectStore("values").put({
|
||||
image,
|
||||
imageAlias: image,
|
||||
pointReadOnly,
|
||||
point,
|
||||
rectReadOnly,
|
||||
rect,
|
||||
quad,
|
||||
matrixReadOnly,
|
||||
matrix
|
||||
}, "value");
|
||||
write.oncomplete = () => {
|
||||
const read = open.result.transaction("values").objectStore("values").get("value");
|
||||
read.onerror = () => {
|
||||
globalThis.__indexedDbGeometryResult =
|
||||
`read-error:${read.error && read.error.name}`;
|
||||
};
|
||||
read.onsuccess = () => {
|
||||
const row = read.result;
|
||||
const exactBrand = (value, name) =>
|
||||
Object.getPrototypeOf(value) === self[name].prototype;
|
||||
const sameValues = (value, names, expected) =>
|
||||
names.every((name, index) => Object.is(value[name], expected[index]));
|
||||
globalThis.__indexedDbGeometryResult = JSON.stringify({
|
||||
image: [
|
||||
exactBrand(row.image, "ImageData"),
|
||||
row.image !== image,
|
||||
row.image === row.imageAlias,
|
||||
row.image.width,
|
||||
row.image.height,
|
||||
row.image.colorSpace,
|
||||
Array.from(row.image.data).join(","),
|
||||
row.image.data !== image.data
|
||||
],
|
||||
pointReadOnly:
|
||||
exactBrand(row.pointReadOnly, "DOMPointReadOnly") &&
|
||||
sameValues(row.pointReadOnly, ["x", "y", "z", "w"], [1, -0, Infinity, NaN]),
|
||||
point:
|
||||
exactBrand(row.point, "DOMPoint") &&
|
||||
sameValues(row.point, ["x", "y", "z", "w"], [2, 3, 4, 5]),
|
||||
rectReadOnly:
|
||||
exactBrand(row.rectReadOnly, "DOMRectReadOnly") &&
|
||||
sameValues(row.rectReadOnly, ["x", "y", "width", "height"], [6, 7, 8, 9]),
|
||||
rect:
|
||||
exactBrand(row.rect, "DOMRect") &&
|
||||
sameValues(row.rect, ["x", "y", "width", "height"], [10, 11, 12, 13]),
|
||||
quad:
|
||||
exactBrand(row.quad, "DOMQuad") &&
|
||||
[row.quad.p1, row.quad.p2, row.quad.p3, row.quad.p4]
|
||||
.every((pointValue, index) =>
|
||||
sameValues(
|
||||
pointValue,
|
||||
["x", "y", "z", "w"],
|
||||
[1 + index * 4, 2 + index * 4, 3 + index * 4, 4 + index * 4]
|
||||
)),
|
||||
matrixReadOnly:
|
||||
exactBrand(row.matrixReadOnly, "DOMMatrixReadOnly") &&
|
||||
sameValues(row.matrixReadOnly, ["a", "b", "c", "d", "e", "f", "is2D"],
|
||||
[1, 2, 3, 4, 5, 6, true]),
|
||||
matrix:
|
||||
exactBrand(row.matrix, "DOMMatrix") &&
|
||||
sameValues(row.matrix, ["m11", "m12", "m13", "m14", "m41", "m42", "m43", "m44", "is2D"],
|
||||
[11, 12, 13, 14, 41, 42, 43, 44, false]),
|
||||
expandosExcluded: [
|
||||
row.image,
|
||||
row.pointReadOnly,
|
||||
row.point,
|
||||
row.rectReadOnly,
|
||||
row.rect,
|
||||
row.quad,
|
||||
row.matrixReadOnly,
|
||||
row.matrix
|
||||
].every(value => value.expando === undefined)
|
||||
});
|
||||
};
|
||||
};
|
||||
};
|
||||
return "scheduled";
|
||||
})()
|
||||
"#,
|
||||
)
|
||||
.expect("indexeddb ImageData/Geometry workflow should schedule");
|
||||
|
||||
let result = vm
|
||||
.eval_after_selected_page_tasks("String(globalThis.__indexedDbGeometryResult)")
|
||||
.expect("indexeddb ImageData/Geometry result should be readable");
|
||||
|
||||
assert_eq!(
|
||||
result,
|
||||
r#"{"image":[true,true,true,2,1,"display-p3","1,2,3,4,5,6,7,8",true],"pointReadOnly":true,"point":true,"rectReadOnly":true,"rect":true,"quad":true,"matrixReadOnly":true,"matrix":true,"expandosExcluded":true}"#
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn indexed_db_roundtrips_opfs_handles_with_durable_external_objects() {
|
||||
let mut vm = new_storage_page_task_executor_test_vm("https://indexeddb-opfs-handle.test/");
|
||||
|
||||
@@ -39,7 +39,7 @@ pub(crate) use moli_structured_clone::{
|
||||
use v8::{ValueDeserializerHelper, ValueSerializerHelper};
|
||||
|
||||
const HOST_OBJECT_TAG_MESSAGE_PORT: u32 = 1;
|
||||
const HOST_OBJECT_TAG_IMAGE_DATA: u32 = 2;
|
||||
pub(crate) const HOST_OBJECT_TAG_IMAGE_DATA: u32 = 2;
|
||||
pub(crate) const HOST_OBJECT_TAG_CRYPTO_KEY: u32 = 3;
|
||||
const HOST_OBJECT_TAG_READABLE_STREAM: u32 = 4;
|
||||
pub(crate) const HOST_OBJECT_TAG_BLOB: u32 = 5;
|
||||
@@ -48,7 +48,7 @@ pub(crate) const HOST_OBJECT_TAG_FILE_SYSTEM_HANDLE: u32 = 7;
|
||||
const HOST_OBJECT_TAG_QUOTA_EXCEEDED_ERROR: u32 = 8;
|
||||
const HOST_OBJECT_TAG_WRITABLE_STREAM: u32 = 9;
|
||||
const HOST_OBJECT_TAG_TRANSFORM_STREAM: u32 = 10;
|
||||
const HOST_OBJECT_TAG_GEOMETRY: u32 = 11;
|
||||
pub(crate) const HOST_OBJECT_TAG_GEOMETRY: u32 = 11;
|
||||
pub(crate) const HOST_OBJECT_TAG_FILE_LIST: u32 = 12;
|
||||
|
||||
const GEOMETRY_KIND_DOM_POINT_READONLY: u32 = 0;
|
||||
@@ -872,7 +872,7 @@ pub(crate) fn build_blob_object_from_clone_payload<'s>(
|
||||
}
|
||||
}
|
||||
|
||||
fn write_image_data_payload(
|
||||
pub(crate) fn write_image_data_payload(
|
||||
serializer: &dyn v8::ValueSerializerHelper,
|
||||
payload: ImageDataClonePayload,
|
||||
) {
|
||||
@@ -883,7 +883,7 @@ fn write_image_data_payload(
|
||||
write_raw_vec(serializer, &payload.bytes);
|
||||
}
|
||||
|
||||
fn read_image_data_payload<'s>(
|
||||
pub(crate) fn read_image_data_payload<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
deserializer: &dyn v8::ValueDeserializerHelper,
|
||||
) -> Option<v8::Local<'s, v8::Object>> {
|
||||
@@ -911,7 +911,7 @@ fn read_image_data_payload<'s>(
|
||||
)
|
||||
}
|
||||
|
||||
fn write_geometry_clone_payload(
|
||||
pub(crate) fn write_geometry_clone_payload(
|
||||
serializer: &dyn v8::ValueSerializerHelper,
|
||||
payload: GeometryClonePayload,
|
||||
) {
|
||||
@@ -964,7 +964,7 @@ fn write_geometry_clone_payload(
|
||||
}
|
||||
}
|
||||
|
||||
fn read_geometry_clone_payload(
|
||||
pub(crate) fn read_geometry_clone_payload(
|
||||
deserializer: &dyn v8::ValueDeserializerHelper,
|
||||
) -> Option<GeometryClonePayload> {
|
||||
let kind = read_u32(deserializer)?;
|
||||
|
||||
Reference in New Issue
Block a user