mirror of
https://github.com/servo/servo.git
synced 2025-08-05 13:40:08 +01:00
support structured cloning for Blob
This commit is contained in:
parent
ec84560e7f
commit
5c4f0be048
4 changed files with 163 additions and 24 deletions
|
@ -5,12 +5,17 @@
|
||||||
//! This module implements structured cloning, as defined by [HTML]
|
//! This module implements structured cloning, as defined by [HTML]
|
||||||
//! (https://html.spec.whatwg.org/multipage/#safe-passing-of-structured-data).
|
//! (https://html.spec.whatwg.org/multipage/#safe-passing-of-structured-data).
|
||||||
|
|
||||||
|
use dom::bindings::conversions::root_from_handleobject;
|
||||||
use dom::bindings::error::{Error, Fallible};
|
use dom::bindings::error::{Error, Fallible};
|
||||||
|
use dom::bindings::js::Root;
|
||||||
|
use dom::bindings::reflector::DomObject;
|
||||||
|
use dom::blob::{Blob, BlobImpl};
|
||||||
use dom::globalscope::GlobalScope;
|
use dom::globalscope::GlobalScope;
|
||||||
use js::jsapi::{Handle, HandleObject, HandleValue, MutableHandleValue};
|
use js::jsapi::{Handle, HandleObject, HandleValue, MutableHandleValue, JSAutoCompartment, JSContext};
|
||||||
use js::jsapi::{Heap, JSContext};
|
|
||||||
use js::jsapi::{JSStructuredCloneCallbacks, JSStructuredCloneReader, JSStructuredCloneWriter};
|
use js::jsapi::{JSStructuredCloneCallbacks, JSStructuredCloneReader, JSStructuredCloneWriter};
|
||||||
use js::jsapi::{JS_ClearPendingException, JSObject, JS_ReadStructuredClone};
|
use js::jsapi::{JS_ClearPendingException, JSObject, JS_ReadStructuredClone};
|
||||||
|
use js::jsapi::{JS_ReadBytes, JS_WriteBytes};
|
||||||
|
use js::jsapi::{JS_ReadUint32Pair, JS_WriteUint32Pair};
|
||||||
use js::jsapi::{JS_STRUCTURED_CLONE_VERSION, JS_WriteStructuredClone};
|
use js::jsapi::{JS_STRUCTURED_CLONE_VERSION, JS_WriteStructuredClone};
|
||||||
use js::jsapi::{MutableHandleObject, TransferableOwnership};
|
use js::jsapi::{MutableHandleObject, TransferableOwnership};
|
||||||
use libc::size_t;
|
use libc::size_t;
|
||||||
|
@ -18,47 +23,127 @@ use std::os::raw;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
use std::slice;
|
use std::slice;
|
||||||
|
|
||||||
|
// TODO: Should we add Min and Max const to https://github.com/servo/rust-mozjs/blob/master/src/consts.rs?
|
||||||
|
// TODO: Determine for sure which value Min and Max should have.
|
||||||
|
// NOTE: Current values found at https://dxr.mozilla.org/mozilla-central/
|
||||||
|
// rev/ff04d410e74b69acfab17ef7e73e7397602d5a68/js/public/StructuredClone.h#323
|
||||||
|
#[repr(u32)]
|
||||||
|
enum StructuredCloneTags {
|
||||||
|
/// To support additional types, add new tags with values incremented from the last one before Max.
|
||||||
|
Min = 0xFFFF8000,
|
||||||
|
DomBlob = 0xFFFF8001,
|
||||||
|
Max = 0xFFFFFFFF,
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[cfg(target_pointer_width = "64")]
|
||||||
unsafe extern "C" fn read_callback(_cx: *mut JSContext,
|
unsafe fn write_length(w: *mut JSStructuredCloneWriter,
|
||||||
_r: *mut JSStructuredCloneReader,
|
length: usize) {
|
||||||
_tag: u32,
|
let high: u32 = (length >> 32) as u32;
|
||||||
|
let low: u32 = length as u32;
|
||||||
|
assert!(JS_WriteUint32Pair(w, high, low));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_pointer_width = "32")]
|
||||||
|
unsafe fn write_length(w: *mut JSStructuredCloneWriter,
|
||||||
|
length: usize) {
|
||||||
|
assert!(JS_WriteUint32Pair(w, length as u32, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_pointer_width = "64")]
|
||||||
|
unsafe fn read_length(r: *mut JSStructuredCloneReader)
|
||||||
|
-> usize {
|
||||||
|
let mut high: u32 = 0;
|
||||||
|
let mut low: u32 = 0;
|
||||||
|
assert!(JS_ReadUint32Pair(r, &mut high as *mut u32, &mut low as *mut u32));
|
||||||
|
return (low << high) as usize;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_pointer_width = "32")]
|
||||||
|
unsafe fn read_length(r: *mut JSStructuredCloneReader)
|
||||||
|
-> usize {
|
||||||
|
let mut length: u32 = 0;
|
||||||
|
let mut zero: u32 = 0;
|
||||||
|
assert!(JS_ReadUint32Pair(r, &mut length as *mut u32, &mut zero as *mut u32));
|
||||||
|
return length as usize;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn read_blob(cx: *mut JSContext,
|
||||||
|
r: *mut JSStructuredCloneReader)
|
||||||
|
-> *mut JSObject {
|
||||||
|
let blob_length = read_length(r);
|
||||||
|
let type_str_length = read_length(r);
|
||||||
|
let mut blob_buffer = vec![0u8; blob_length];
|
||||||
|
assert!(JS_ReadBytes(r, blob_buffer.as_mut_ptr() as *mut raw::c_void, blob_length));
|
||||||
|
let mut type_str_buffer = vec![0u8; type_str_length];
|
||||||
|
assert!(JS_ReadBytes(r, type_str_buffer.as_mut_ptr() as *mut raw::c_void, type_str_length));
|
||||||
|
let type_str = String::from_utf8_unchecked(type_str_buffer);
|
||||||
|
let target_global = GlobalScope::from_context(cx);
|
||||||
|
let blob = Blob::new(&target_global, BlobImpl::new_from_bytes(blob_buffer), type_str);
|
||||||
|
return blob.reflector().get_jsobject().get()
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn write_blob(blob: Root<Blob>,
|
||||||
|
w: *mut JSStructuredCloneWriter)
|
||||||
|
-> Result<(), ()> {
|
||||||
|
let blob_vec = try!(blob.get_bytes());
|
||||||
|
let blob_length = blob_vec.len();
|
||||||
|
let type_string_bytes = blob.get_type_string().as_bytes().to_vec();
|
||||||
|
let type_string_length = type_string_bytes.len();
|
||||||
|
assert!(JS_WriteUint32Pair(w, StructuredCloneTags::DomBlob as u32, 0));
|
||||||
|
write_length(w, blob_length);
|
||||||
|
write_length(w, type_string_length);
|
||||||
|
assert!(JS_WriteBytes(w, blob_vec.as_ptr() as *const raw::c_void, blob_length));
|
||||||
|
assert!(JS_WriteBytes(w, type_string_bytes.as_ptr() as *const raw::c_void, type_string_length));
|
||||||
|
return Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "C" fn read_callback(cx: *mut JSContext,
|
||||||
|
r: *mut JSStructuredCloneReader,
|
||||||
|
tag: u32,
|
||||||
_data: u32,
|
_data: u32,
|
||||||
_closure: *mut raw::c_void) -> *mut JSObject {
|
_closure: *mut raw::c_void)
|
||||||
Heap::default().get()
|
-> *mut JSObject {
|
||||||
|
assert!(tag < StructuredCloneTags::Max as u32, "tag should be lower than StructuredCloneTags::Max");
|
||||||
|
assert!(tag > StructuredCloneTags::Min as u32, "tag should be higher than StructuredCloneTags::Min");
|
||||||
|
if tag == StructuredCloneTags::DomBlob as u32 {
|
||||||
|
return read_blob(cx, r)
|
||||||
|
}
|
||||||
|
return ptr::null_mut()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
unsafe extern "C" fn write_callback(_cx: *mut JSContext,
|
unsafe extern "C" fn write_callback(_cx: *mut JSContext,
|
||||||
_w: *mut JSStructuredCloneWriter,
|
w: *mut JSStructuredCloneWriter,
|
||||||
_obj: HandleObject,
|
obj: HandleObject,
|
||||||
_closure: *mut raw::c_void) -> bool {
|
_closure: *mut raw::c_void)
|
||||||
false
|
-> bool {
|
||||||
|
if let Ok(blob) = root_from_handleobject::<Blob>(obj) {
|
||||||
|
return write_blob(blob, w).is_ok()
|
||||||
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
unsafe extern "C" fn read_transfer_callback(_cx: *mut JSContext,
|
unsafe extern "C" fn read_transfer_callback(_cx: *mut JSContext,
|
||||||
_r: *mut JSStructuredCloneReader,
|
_r: *mut JSStructuredCloneReader,
|
||||||
_tag: u32,
|
_tag: u32,
|
||||||
_content: *mut raw::c_void,
|
_content: *mut raw::c_void,
|
||||||
_extra_data: u64,
|
_extra_data: u64,
|
||||||
_closure: *mut raw::c_void,
|
_closure: *mut raw::c_void,
|
||||||
_return_object: MutableHandleObject) -> bool {
|
_return_object: MutableHandleObject)
|
||||||
|
-> bool {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
unsafe extern "C" fn write_transfer_callback(_cx: *mut JSContext,
|
unsafe extern "C" fn write_transfer_callback(_cx: *mut JSContext,
|
||||||
_obj: Handle<*mut JSObject>,
|
_obj: Handle<*mut JSObject>,
|
||||||
_closure: *mut raw::c_void,
|
_closure: *mut raw::c_void,
|
||||||
_tag: *mut u32,
|
_tag: *mut u32,
|
||||||
_ownership: *mut TransferableOwnership,
|
_ownership: *mut TransferableOwnership,
|
||||||
_content: *mut *mut raw::c_void,
|
_content: *mut *mut raw::c_void,
|
||||||
_extra_data: *mut u64) -> bool {
|
_extra_data: *mut u64)
|
||||||
|
-> bool {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
unsafe extern "C" fn free_transfer_callback(_tag: u32,
|
unsafe extern "C" fn free_transfer_callback(_tag: u32,
|
||||||
_ownership: TransferableOwnership,
|
_ownership: TransferableOwnership,
|
||||||
_content: *mut raw::c_void,
|
_content: *mut raw::c_void,
|
||||||
|
@ -66,11 +151,9 @@ unsafe extern "C" fn free_transfer_callback(_tag: u32,
|
||||||
_closure: *mut raw::c_void) {
|
_closure: *mut raw::c_void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
unsafe extern "C" fn report_error_callback(_cx: *mut JSContext, _errorid: u32) {
|
unsafe extern "C" fn report_error_callback(_cx: *mut JSContext, _errorid: u32) {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
static STRUCTURED_CLONE_CALLBACKS: JSStructuredCloneCallbacks = JSStructuredCloneCallbacks {
|
static STRUCTURED_CLONE_CALLBACKS: JSStructuredCloneCallbacks = JSStructuredCloneCallbacks {
|
||||||
read: Some(read_callback),
|
read: Some(read_callback),
|
||||||
write: Some(write_callback),
|
write: Some(write_callback),
|
||||||
|
@ -98,7 +181,7 @@ impl StructuredCloneData {
|
||||||
message,
|
message,
|
||||||
&mut data,
|
&mut data,
|
||||||
&mut nbytes,
|
&mut nbytes,
|
||||||
ptr::null(),
|
&STRUCTURED_CLONE_CALLBACKS,
|
||||||
ptr::null_mut(),
|
ptr::null_mut(),
|
||||||
HandleValue::undefined())
|
HandleValue::undefined())
|
||||||
};
|
};
|
||||||
|
@ -126,14 +209,20 @@ impl StructuredCloneData {
|
||||||
/// Reads a structured clone.
|
/// Reads a structured clone.
|
||||||
///
|
///
|
||||||
/// Panics if `JS_ReadStructuredClone` fails.
|
/// Panics if `JS_ReadStructuredClone` fails.
|
||||||
fn read_clone(global: &GlobalScope, data: *mut u64, nbytes: size_t, rval: MutableHandleValue) {
|
fn read_clone(global: &GlobalScope,
|
||||||
|
data: *mut u64,
|
||||||
|
nbytes: size_t,
|
||||||
|
rval: MutableHandleValue) {
|
||||||
|
let cx = global.get_cx();
|
||||||
|
let globalhandle = global.reflector().get_jsobject();
|
||||||
|
let _ac = JSAutoCompartment::new(cx, globalhandle.get());
|
||||||
unsafe {
|
unsafe {
|
||||||
assert!(JS_ReadStructuredClone(global.get_cx(),
|
assert!(JS_ReadStructuredClone(cx,
|
||||||
data,
|
data,
|
||||||
nbytes,
|
nbytes,
|
||||||
JS_STRUCTURED_CLONE_VERSION,
|
JS_STRUCTURED_CLONE_VERSION,
|
||||||
rval,
|
rval,
|
||||||
ptr::null(),
|
&STRUCTURED_CLONE_CALLBACKS,
|
||||||
ptr::null_mut()));
|
ptr::null_mut()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -166,6 +166,11 @@ impl Blob {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get a copy of the type_string
|
||||||
|
pub fn get_type_string(&self) -> String {
|
||||||
|
self.type_string.clone()
|
||||||
|
}
|
||||||
|
|
||||||
/// Get a FileID representing the Blob content,
|
/// Get a FileID representing the Blob content,
|
||||||
/// used by URL.createObjectURL
|
/// used by URL.createObjectURL
|
||||||
pub fn get_blob_url_id(&self) -> Uuid {
|
pub fn get_blob_url_id(&self) -> Uuid {
|
||||||
|
|
|
@ -90879,6 +90879,12 @@
|
||||||
{}
|
{}
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
|
"html/infrastructure/safe-passing-of-structured-data/structured_clone_blob.html": [
|
||||||
|
[
|
||||||
|
"/html/infrastructure/safe-passing-of-structured-data/structured_clone_blob.html",
|
||||||
|
{}
|
||||||
|
]
|
||||||
|
],
|
||||||
"html/infrastructure/terminology/plugins/text-plain.html": [
|
"html/infrastructure/terminology/plugins/text-plain.html": [
|
||||||
[
|
[
|
||||||
"/html/infrastructure/terminology/plugins/text-plain.html",
|
"/html/infrastructure/terminology/plugins/text-plain.html",
|
||||||
|
@ -173218,6 +173224,10 @@
|
||||||
"da39a3ee5e6b4b0d3255bfef95601890afd80709",
|
"da39a3ee5e6b4b0d3255bfef95601890afd80709",
|
||||||
"support"
|
"support"
|
||||||
],
|
],
|
||||||
|
"html/infrastructure/safe-passing-of-structured-data/structured_clone_blob.html": [
|
||||||
|
"2a3deba2534cad6f5e0aa85cfc3c90debcead20a",
|
||||||
|
"testharness"
|
||||||
|
],
|
||||||
"html/infrastructure/terminology/.gitkeep": [
|
"html/infrastructure/terminology/.gitkeep": [
|
||||||
"da39a3ee5e6b4b0d3255bfef95601890afd80709",
|
"da39a3ee5e6b4b0d3255bfef95601890afd80709",
|
||||||
"support"
|
"support"
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Safe passing of structured data - Blob</title>
|
||||||
|
<script src="/resources/testharness.js"></script>
|
||||||
|
<script src="/resources/testharnessreport.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<script>
|
||||||
|
async_test(function(t) {
|
||||||
|
var blob = new Blob(['<a id="a"><b id="b">hey!</b></a>'], {type:"text/plain"});
|
||||||
|
window.addEventListener("message", this.step_func(function(msg) {
|
||||||
|
assert_true(msg.data instanceof Blob);
|
||||||
|
assert_equals(msg.data.size, blob.size);
|
||||||
|
assert_equals(msg.data.type, blob.type);
|
||||||
|
var cloned_content, original_content;
|
||||||
|
var reader = new FileReader();
|
||||||
|
reader.addEventListener("loadend", this.step_func(function() {
|
||||||
|
original_content = reader.result;
|
||||||
|
var reader2 = new FileReader();
|
||||||
|
reader2.addEventListener("loadend", this.step_func_done(function() {
|
||||||
|
cloned_content = reader2.result;
|
||||||
|
assert_equals(typeof cloned_content, typeof original_content);
|
||||||
|
assert_equals(cloned_content, original_content);
|
||||||
|
}));
|
||||||
|
reader2.readAsText(msg.data);
|
||||||
|
}));
|
||||||
|
reader.readAsText(blob);
|
||||||
|
}), false);
|
||||||
|
window.postMessage(blob, '*');
|
||||||
|
}, "Cloning a Blob into the same realm");
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Add table
Add a link
Reference in a new issue