mirror of
https://github.com/lexmount/moli.git
synced 2026-09-28 16:01:39 +00:00
fix(blob): preserve receiver realm for slice results
This commit is contained in:
@@ -15,6 +15,7 @@ use super::{
|
||||
|
||||
const BLOB_ID_SLOT: &str = "__lmBlobId";
|
||||
const BLOB_PROTOTYPE_SLOT: &str = "__lmBlobPrototype";
|
||||
const BLOB_REALM_PROTOTYPE_SLOT: &str = "__lmBlobRealmPrototype";
|
||||
|
||||
fn native_blob_line_ending() -> &'static str {
|
||||
if moli_browser_profile::DEFAULT_WINDOW_SURFACE_PROFILE
|
||||
@@ -225,7 +226,11 @@ pub(super) fn blob_slice_callback<'s>(
|
||||
.map(ToOwned::to_owned)
|
||||
.unwrap_or_default();
|
||||
|
||||
if let Some(blob) = build_blob_object(scope, sliced, mime_type) {
|
||||
let prototype = blob_realm_prototype_from_object(scope, args.this())
|
||||
.or_else(|| current_realm_blob_prototype(scope));
|
||||
if let Some(blob) = prototype
|
||||
.and_then(|prototype| build_blob_object_with_prototype(scope, sliced, mime_type, prototype))
|
||||
{
|
||||
rv.set(blob.into());
|
||||
} else {
|
||||
rv.set(v8::undefined(scope).into());
|
||||
@@ -244,6 +249,9 @@ pub(super) fn init_blob_object<'s>(
|
||||
BlobInstanceDeclaration::new(v8::BigInt::new_from_u64(scope, blob_id))
|
||||
.initialize(scope, object)
|
||||
.expect("Blob instance declaration should initialize");
|
||||
if let Some(prototype) = current_realm_blob_prototype(scope) {
|
||||
set_private_value(scope, object, BLOB_REALM_PROTOTYPE_SLOT, prototype.into());
|
||||
}
|
||||
track_blob_ref_lifetime(scope, object, move || release_blob_wrapper_ref(blob_id));
|
||||
blob_id
|
||||
}
|
||||
@@ -252,15 +260,40 @@ pub(super) fn build_blob_object<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
bytes: Vec<u8>,
|
||||
mime_type: String,
|
||||
) -> Option<v8::Local<'s, v8::Object>> {
|
||||
let prototype = current_realm_blob_prototype(scope)?;
|
||||
build_blob_object_with_prototype(scope, bytes, mime_type, prototype)
|
||||
}
|
||||
|
||||
fn current_realm_blob_prototype<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
) -> Option<v8::Local<'s, v8::Object>> {
|
||||
let global = scope.get_current_context().global(scope);
|
||||
let prototype = get_private_value(scope, global, BLOB_PROTOTYPE_SLOT).or_else(|| {
|
||||
let prototype =
|
||||
crate::context_bootstrap::ensure_intrinsic_interface_prototype(scope, "Blob").ok()?;
|
||||
finalize_blob_realm_bindings(scope, prototype);
|
||||
Some(prototype.into())
|
||||
});
|
||||
let prototype = prototype?;
|
||||
get_private_value(scope, global, BLOB_PROTOTYPE_SLOT)
|
||||
.and_then(|value| v8::Local::<v8::Object>::try_from(value).ok())
|
||||
.or_else(|| {
|
||||
let prototype =
|
||||
crate::context_bootstrap::ensure_intrinsic_interface_prototype(scope, "Blob")
|
||||
.ok()?;
|
||||
finalize_blob_realm_bindings(scope, prototype);
|
||||
Some(prototype)
|
||||
})
|
||||
}
|
||||
|
||||
fn blob_realm_prototype_from_object<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
object: v8::Local<'s, v8::Object>,
|
||||
) -> Option<v8::Local<'s, v8::Object>> {
|
||||
get_private_value(scope, object, BLOB_REALM_PROTOTYPE_SLOT)
|
||||
.and_then(|value| v8::Local::<v8::Object>::try_from(value).ok())
|
||||
}
|
||||
|
||||
fn build_blob_object_with_prototype<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
bytes: Vec<u8>,
|
||||
mime_type: String,
|
||||
prototype: v8::Local<'s, v8::Object>,
|
||||
) -> Option<v8::Local<'s, v8::Object>> {
|
||||
let owner_id = current_resource_owner_id(scope);
|
||||
let partition_id = current_blob_storage_partition_identity(scope);
|
||||
let blob_id = blob_store().create_blob(owner_id, partition_id, bytes, mime_type);
|
||||
@@ -271,7 +304,8 @@ pub(super) fn build_blob_object<'s>(
|
||||
release_blob_wrapper_ref(blob_id);
|
||||
None
|
||||
})?;
|
||||
let _ = object.set_prototype(scope, prototype);
|
||||
let _ = object.set_prototype(scope, prototype.into());
|
||||
set_private_value(scope, object, BLOB_REALM_PROTOTYPE_SLOT, prototype.into());
|
||||
track_blob_ref_lifetime(scope, object, move || release_blob_wrapper_ref(blob_id));
|
||||
Some(object)
|
||||
}
|
||||
|
||||
@@ -3129,6 +3129,70 @@ fn blob_internal_builders_survive_global_blob_override() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn blob_slice_uses_receiver_realm_after_method_realm_is_detached() {
|
||||
let mut vm = new_parsed_test_vm(
|
||||
"https://blob-slice-receiver-realm.test/",
|
||||
"<!doctype html><html><body></body></html>",
|
||||
);
|
||||
|
||||
vm.eval(
|
||||
r#"
|
||||
(() => {
|
||||
const iframe = document.createElement("iframe");
|
||||
iframe.srcdoc = "<!doctype html><html><body></body></html>";
|
||||
document.body.appendChild(iframe);
|
||||
globalThis.__blobSliceRealmFrame = iframe;
|
||||
})()
|
||||
"#,
|
||||
)
|
||||
.expect("detached-Realm Blob slice setup should evaluate");
|
||||
vm.drain_pending_child_frame_work_for_test();
|
||||
|
||||
vm.eval(
|
||||
r#"
|
||||
(() => {
|
||||
const iframe = __blobSliceRealmFrame;
|
||||
const detachedSlice = iframe.contentWindow.Blob.prototype.slice;
|
||||
iframe.remove();
|
||||
|
||||
const blobSlice = detachedSlice.call(new Blob(["abcdef"]), 1, 4, "Text/Custom");
|
||||
const fileSlice = detachedSlice.call(new File(["uvwxyz"], "sample.txt"), 2, 5);
|
||||
globalThis.__blobSliceRealmProbe = {
|
||||
childDetached: iframe.contentWindow === null,
|
||||
blobIsMainRealmBlob: blobSlice instanceof Blob,
|
||||
blobPrototypeIsMainRealmBlob: Object.getPrototypeOf(blobSlice) === Blob.prototype,
|
||||
fileSliceIsMainRealmBlob: fileSlice instanceof Blob,
|
||||
fileSliceIsFile: fileSlice instanceof File,
|
||||
fileSlicePrototypeIsMainRealmBlob: Object.getPrototypeOf(fileSlice) === Blob.prototype,
|
||||
type: blobSlice.type
|
||||
};
|
||||
Promise.all([blobSlice.text(), fileSlice.text()]).then(
|
||||
([blobText, fileText]) => {
|
||||
__blobSliceRealmProbe.blobText = blobText;
|
||||
__blobSliceRealmProbe.fileText = fileText;
|
||||
},
|
||||
error => { __blobSliceRealmProbe.error = error && error.name; }
|
||||
);
|
||||
return "scheduled";
|
||||
})()
|
||||
"#,
|
||||
)
|
||||
.expect("detached-Realm Blob slice probe should evaluate");
|
||||
|
||||
vm.eval("0")
|
||||
.expect("detached-Realm Blob slice promise microtasks should drain");
|
||||
|
||||
let result = vm
|
||||
.eval("JSON.stringify(globalThis.__blobSliceRealmProbe)")
|
||||
.expect("detached-Realm Blob slice result should evaluate");
|
||||
|
||||
assert_eq!(
|
||||
result,
|
||||
r#"{"childDetached":true,"blobIsMainRealmBlob":true,"blobPrototypeIsMainRealmBlob":true,"fileSliceIsMainRealmBlob":true,"fileSliceIsFile":false,"fileSlicePrototypeIsMainRealmBlob":true,"type":"text/custom","blobText":"bcd","fileText":"wxy"}"#
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn blob_stream_is_native_readable_stream_and_response_consumes_bytes() {
|
||||
let mut vm = new_storage_test_vm("https://blob-stream-reader.test/");
|
||||
|
||||
Reference in New Issue
Block a user