aboutsummaryrefslogtreecommitdiffstats
path: root/components/script
diff options
context:
space:
mode:
authorbors-servo <lbergstrom+bors@mozilla.com>2019-12-19 16:16:56 -0500
committerGitHub <noreply@github.com>2019-12-19 16:16:56 -0500
commitbac9903fbeed0a394a86c0091e727aada665433d (patch)
tree15f61a4fdcc8bbcc09f10a39e25495fb49cb9a75 /components/script
parentaa36d5f657eb32b59d994d08b177a02372b4b01c (diff)
parent6e8a85482c2068d4dbccb992954271f725570f91 (diff)
downloadservo-bac9903fbeed0a394a86c0091e727aada665433d.tar.gz
servo-bac9903fbeed0a394a86c0091e727aada665433d.zip
Auto merge of #24123 - gterzian:redo_blob, r=jdm
Restructure Blob, structured serialization <!-- Please describe your changes on the following line: --> FIX #24052 and also address the "cloning" half of FIX #23917 --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `___` with appropriate data: --> - [ ] `./mach build -d` does not report any errors - [ ] `./mach test-tidy` does not report any errors - [ ] These changes fix #___ (GitHub issue number if applicable) <!-- Either: --> - [ ] There are tests for these changes OR - [ ] These changes do not require tests because ___ <!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.--> <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. --> <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/24123) <!-- Reviewable:end -->
Diffstat (limited to 'components/script')
-rw-r--r--components/script/body.rs8
-rw-r--r--components/script/dom/abstractworkerglobalscope.rs2
-rw-r--r--components/script/dom/bindings/codegen/Bindings.conf8
-rw-r--r--components/script/dom/bindings/mod.rs1
-rw-r--r--components/script/dom/bindings/serializable.rs32
-rw-r--r--components/script/dom/bindings/structuredclone.rs201
-rw-r--r--components/script/dom/bindings/trace.rs6
-rw-r--r--components/script/dom/blob.rs378
-rw-r--r--components/script/dom/dedicatedworkerglobalscope.rs2
-rw-r--r--components/script/dom/document.rs3
-rw-r--r--components/script/dom/file.rs34
-rw-r--r--components/script/dom/formdata.rs6
-rw-r--r--components/script/dom/globalscope.rs448
-rw-r--r--components/script/dom/history.rs1
-rw-r--r--components/script/dom/messageport.rs2
-rw-r--r--components/script/dom/testbinding.rs18
-rw-r--r--components/script/dom/websocket.rs5
-rw-r--r--components/script/dom/xmlhttprequest.rs7
-rw-r--r--components/script/script_thread.rs2
19 files changed, 725 insertions, 439 deletions
diff --git a/components/script/body.rs b/components/script/body.rs
index 728315bf1fa..e38dd1b0a98 100644
--- a/components/script/body.rs
+++ b/components/script/body.rs
@@ -9,7 +9,7 @@ use crate::dom::bindings::reflector::DomObject;
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::str::USVString;
use crate::dom::bindings::trace::RootedTraceableBox;
-use crate::dom::blob::{Blob, BlobImpl};
+use crate::dom::blob::{normalize_type_string, Blob};
use crate::dom::formdata::FormData;
use crate::dom::globalscope::GlobalScope;
use crate::dom::promise::Promise;
@@ -24,6 +24,7 @@ use js::rust::wrappers::JS_GetPendingException;
use js::rust::wrappers::JS_ParseJSON;
use js::typedarray::{ArrayBuffer, CreateWith};
use mime::{self, Mime};
+use script_traits::serializable::BlobImpl;
use std::cell::Ref;
use std::ptr;
use std::rc::Rc;
@@ -166,7 +167,10 @@ fn run_blob_data_algorithm(
} else {
"".to_string()
};
- let blob = Blob::new(root, BlobImpl::new_from_bytes(bytes), mime_string);
+ let blob = Blob::new(
+ root,
+ BlobImpl::new_from_bytes(bytes, normalize_type_string(&mime_string)),
+ );
Ok(FetchedData::BlobData(blob))
}
diff --git a/components/script/dom/abstractworkerglobalscope.rs b/components/script/dom/abstractworkerglobalscope.rs
index d5186f91845..336bf1996e7 100644
--- a/components/script/dom/abstractworkerglobalscope.rs
+++ b/components/script/dom/abstractworkerglobalscope.rs
@@ -148,5 +148,5 @@ pub fn run_worker_event_loop<T, WorkerMsg, Event>(
}
worker_scope
.upcast::<GlobalScope>()
- .perform_a_message_port_garbage_collection_checkpoint();
+ .perform_a_dom_garbage_collection_checkpoint();
}
diff --git a/components/script/dom/bindings/codegen/Bindings.conf b/components/script/dom/bindings/codegen/Bindings.conf
index 31a041945fd..3a5f8576cef 100644
--- a/components/script/dom/bindings/codegen/Bindings.conf
+++ b/components/script/dom/bindings/codegen/Bindings.conf
@@ -14,6 +14,14 @@
DOMInterfaces = {
+'Blob': {
+ 'weakReferenceable': True,
+},
+
+'File': {
+ 'weakReferenceable': True,
+},
+
'MediaQueryList': {
'weakReferenceable': True,
},
diff --git a/components/script/dom/bindings/mod.rs b/components/script/dom/bindings/mod.rs
index 0bcdbf4fcfd..8b3f9682544 100644
--- a/components/script/dom/bindings/mod.rs
+++ b/components/script/dom/bindings/mod.rs
@@ -150,6 +150,7 @@ pub mod record;
pub mod refcounted;
pub mod reflector;
pub mod root;
+pub mod serializable;
pub mod settings_stack;
pub mod str;
pub mod structuredclone;
diff --git a/components/script/dom/bindings/serializable.rs b/components/script/dom/bindings/serializable.rs
new file mode 100644
index 00000000000..004b9ef4d73
--- /dev/null
+++ b/components/script/dom/bindings/serializable.rs
@@ -0,0 +1,32 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//! Trait representing the concept of [serializable objects]
+//! (https://html.spec.whatwg.org/multipage/#serializable-objects).
+
+use crate::dom::bindings::reflector::DomObject;
+use crate::dom::bindings::root::DomRoot;
+use crate::dom::bindings::structuredclone::StructuredDataHolder;
+use crate::dom::globalscope::GlobalScope;
+
+/// The key corresponding to the storage location
+/// of a serialized platform object stored in a StructuredDataHolder.
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+pub struct StorageKey {
+ pub index: u32,
+ pub name_space: u32,
+}
+
+/// Interface for serializable platform objects.
+/// <https://html.spec.whatwg.org/multipage/#serializable>
+pub trait Serializable: DomObject {
+ /// <https://html.spec.whatwg.org/multipage/#serialization-steps>
+ fn serialize(&self, sc_holder: &mut StructuredDataHolder) -> Result<StorageKey, ()>;
+ /// <https://html.spec.whatwg.org/multipage/#deserialization-steps>
+ fn deserialize(
+ owner: &DomRoot<GlobalScope>,
+ sc_holder: &mut StructuredDataHolder,
+ extra_data: StorageKey,
+ ) -> Result<(), ()>;
+}
diff --git a/components/script/dom/bindings/structuredclone.rs b/components/script/dom/bindings/structuredclone.rs
index 25c440365c8..a61cdb0630c 100644
--- a/components/script/dom/bindings/structuredclone.rs
+++ b/components/script/dom/bindings/structuredclone.rs
@@ -10,8 +10,9 @@ use crate::dom::bindings::conversions::{root_from_object, ToJSValConvertible};
use crate::dom::bindings::error::{Error, Fallible};
use crate::dom::bindings::reflector::DomObject;
use crate::dom::bindings::root::DomRoot;
+use crate::dom::bindings::serializable::{Serializable, StorageKey};
use crate::dom::bindings::transferable::Transferable;
-use crate::dom::blob::{Blob, BlobImpl};
+use crate::dom::blob::Blob;
use crate::dom::globalscope::GlobalScope;
use crate::dom::messageport::MessagePort;
use crate::script_runtime::JSContext as SafeJSContext;
@@ -29,12 +30,12 @@ use js::jsapi::TransferableOwnership;
use js::jsapi::JS_STRUCTURED_CLONE_VERSION;
use js::jsapi::{JSObject, JS_ClearPendingException};
use js::jsapi::{JSStructuredCloneCallbacks, JSStructuredCloneReader, JSStructuredCloneWriter};
-use js::jsapi::{JS_ReadBytes, JS_WriteBytes};
use js::jsapi::{JS_ReadUint32Pair, JS_WriteUint32Pair};
use js::jsval::UndefinedValue;
use js::rust::wrappers::{JS_ReadStructuredClone, JS_WriteStructuredClone};
use js::rust::{CustomAutoRooterGuard, HandleValue, MutableHandleValue};
-use msg::constellation_msg::MessagePortId;
+use msg::constellation_msg::{BlobId, MessagePortId};
+use script_traits::serializable::BlobImpl;
use script_traits::transferable::MessagePortImpl;
use script_traits::StructuredSerializedData;
use std::collections::HashMap;
@@ -54,117 +55,62 @@ enum StructuredCloneTags {
Max = 0xFFFFFFFF,
}
-#[cfg(target_pointer_width = "64")]
-unsafe fn write_length(w: *mut JSStructuredCloneWriter, length: usize) {
- 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;
+unsafe fn read_blob(
+ owner: &DomRoot<GlobalScope>,
+ r: *mut JSStructuredCloneReader,
+ mut sc_holder: &mut StructuredDataHolder,
+) -> *mut JSObject {
+ let mut name_space: u32 = 0;
+ let mut index: u32 = 0;
assert!(JS_ReadUint32Pair(
r,
- &mut length as *mut u32,
- &mut zero as *mut u32
+ &mut name_space as *mut u32,
+ &mut index as *mut u32
));
- return length as usize;
+ let storage_key = StorageKey { index, name_space };
+ if <Blob as Serializable>::deserialize(&owner, &mut sc_holder, storage_key.clone()).is_ok() {
+ let blobs = match sc_holder {
+ StructuredDataHolder::Read { blobs, .. } => blobs,
+ _ => panic!("Unexpected variant of StructuredDataHolder"),
+ };
+ if let Some(blobs) = blobs {
+ let blob = blobs
+ .get(&storage_key)
+ .expect("No blob found at storage key.");
+ return blob.reflector().get_jsobject().get();
+ }
+ }
+ warn!(
+ "Reading structured data for a blob failed in {:?}.",
+ owner.get_url()
+ );
+ ptr::null_mut()
}
-struct StructuredCloneWriter {
+unsafe fn write_blob(
+ owner: &DomRoot<GlobalScope>,
+ blob: DomRoot<Blob>,
w: *mut JSStructuredCloneWriter,
-}
-
-impl StructuredCloneWriter {
- unsafe fn write_slice(&self, v: &[u8]) {
- let type_length = v.len();
- write_length(self.w, type_length);
- assert!(JS_WriteBytes(
- self.w,
- v.as_ptr() as *const raw::c_void,
- type_length
+ sc_holder: &mut StructuredDataHolder,
+) -> bool {
+ if let Ok(storage_key) = blob.serialize(sc_holder) {
+ assert!(JS_WriteUint32Pair(
+ w,
+ StructuredCloneTags::DomBlob as u32,
+ 0
));
- }
- unsafe fn write_str(&self, s: &str) {
- self.write_slice(s.as_bytes());
- }
-}
-
-struct StructuredCloneReader {
- r: *mut JSStructuredCloneReader,
-}
-
-impl StructuredCloneReader {
- unsafe fn read_bytes(&self) -> Vec<u8> {
- let mut bytes = vec![0u8; read_length(self.r)];
- let blob_length = bytes.len();
- assert!(JS_ReadBytes(
- self.r,
- bytes.as_mut_ptr() as *mut raw::c_void,
- blob_length
+ assert!(JS_WriteUint32Pair(
+ w,
+ storage_key.name_space,
+ storage_key.index
));
- return bytes;
- }
- unsafe fn read_str(&self) -> String {
- let str_buffer = self.read_bytes();
- return String::from_utf8_unchecked(str_buffer);
+ return true;
}
-}
-
-unsafe fn read_blob(
- cx: *mut JSContext,
- r: *mut JSStructuredCloneReader,
- sc_holder: &mut StructuredDataHolder,
-) -> *mut JSObject {
- let structured_reader = StructuredCloneReader { r: r };
- let blob_buffer = structured_reader.read_bytes();
- let type_str = structured_reader.read_str();
- let target_global = GlobalScope::from_context(cx);
- let read_blob = Blob::new(
- &target_global,
- BlobImpl::new_from_bytes(blob_buffer),
- type_str,
+ warn!(
+ "Writing structured data for a blob failed in {:?}.",
+ owner.get_url()
);
- let js_object = read_blob.reflector().get_jsobject().get();
- match sc_holder {
- StructuredDataHolder::Read { blob, .. } => {
- *blob = Some(read_blob);
- },
- _ => panic!("Unexpected variant of StructuredDataHolder"),
- }
- js_object
-}
-
-unsafe fn write_blob(blob: DomRoot<Blob>, w: *mut JSStructuredCloneWriter) -> Result<(), ()> {
- let structured_writer = StructuredCloneWriter { w: w };
- let blob_vec = blob.get_bytes()?;
- assert!(JS_WriteUint32Pair(
- w,
- StructuredCloneTags::DomBlob as u32,
- 0
- ));
- structured_writer.write_slice(&blob_vec);
- structured_writer.write_str(&blob.type_string());
- return Ok(());
+ return false;
}
unsafe extern "C" fn read_callback(
@@ -183,7 +129,11 @@ unsafe extern "C" fn read_callback(
"tag should be higher than StructuredCloneTags::Min"
);
if tag == StructuredCloneTags::DomBlob as u32 {
- return read_blob(cx, r, &mut *(closure as *mut StructuredDataHolder));
+ return read_blob(
+ &GlobalScope::from_context(cx),
+ r,
+ &mut *(closure as *mut StructuredDataHolder),
+ );
}
return ptr::null_mut();
}
@@ -192,10 +142,15 @@ unsafe extern "C" fn write_callback(
cx: *mut JSContext,
w: *mut JSStructuredCloneWriter,
obj: RawHandleObject,
- _closure: *mut raw::c_void,
+ closure: *mut raw::c_void,
) -> bool {
if let Ok(blob) = root_from_object::<Blob>(*obj, cx) {
- return write_blob(blob, w).is_ok();
+ return write_blob(
+ &GlobalScope::from_context(cx),
+ blob,
+ w,
+ &mut *(closure as *mut StructuredDataHolder),
+ );
}
return false;
}
@@ -282,8 +237,8 @@ static STRUCTURED_CLONE_CALLBACKS: JSStructuredCloneCallbacks = JSStructuredClon
/// https://html.spec.whatwg.org/multipage/#safe-passing-of-structured-data
pub enum StructuredDataHolder {
Read {
- /// A deserialized blob, stored temporarily here to keep it rooted.
- blob: Option<DomRoot<Blob>>,
+ /// A map of deserialized blobs, stored temporarily here to keep them rooted.
+ blobs: Option<HashMap<StorageKey, DomRoot<Blob>>>,
/// A vec of transfer-received DOM ports,
/// to be made available to script through a message event.
message_ports: Option<Vec<DomRoot<MessagePort>>>,
@@ -291,10 +246,18 @@ pub enum StructuredDataHolder {
/// used as part of the "transfer-receiving" steps of ports,
/// to produce the DOM ports stored in `message_ports` above.
port_impls: Option<HashMap<MessagePortId, MessagePortImpl>>,
+ /// A map of blob implementations,
+ /// used as part of the "deserialize" steps of blobs,
+ /// to produce the DOM blobs stored in `blobs` above.
+ blob_impls: Option<HashMap<BlobId, BlobImpl>>,
+ },
+ /// A data holder for transferred and serialized objects.
+ Write {
+ /// Transferred ports.
+ ports: Option<HashMap<MessagePortId, MessagePortImpl>>,
+ /// Serialized blobs.
+ blobs: Option<HashMap<BlobId, BlobImpl>>,
},
- /// A data holder into which transferred ports
- /// can be written as part of their transfer steps.
- Write(Option<HashMap<MessagePortId, MessagePortImpl>>),
}
/// Writes a structured clone. Returns a `DataClone` error if that fails.
@@ -308,8 +271,10 @@ pub fn write(
if let Some(transfer) = transfer {
transfer.to_jsval(*cx, val.handle_mut());
}
-
- let mut sc_holder = StructuredDataHolder::Write(None);
+ let mut sc_holder = StructuredDataHolder::Write {
+ ports: None,
+ blobs: None,
+ };
let sc_holder_ptr = &mut sc_holder as *mut _;
let scbuf = NewJSAutoStructuredCloneBuffer(
@@ -343,14 +308,15 @@ pub fn write(
DeleteJSAutoStructuredCloneBuffer(scbuf);
- let mut port_impls = match sc_holder {
- StructuredDataHolder::Write(port_impls) => port_impls,
+ let (mut blob_impls, mut port_impls) = match sc_holder {
+ StructuredDataHolder::Write { blobs, ports } => (blobs, ports),
_ => panic!("Unexpected variant of StructuredDataHolder"),
};
let data = StructuredSerializedData {
serialized: data,
ports: port_impls.take(),
+ blobs: blob_impls.take(),
};
Ok(data)
@@ -367,9 +333,10 @@ pub fn read(
let cx = global.get_cx();
let _ac = enter_realm(&*global);
let mut sc_holder = StructuredDataHolder::Read {
- blob: None,
+ blobs: None,
message_ports: None,
port_impls: data.ports.take(),
+ blob_impls: data.blobs.take(),
};
let sc_holder_ptr = &mut sc_holder as *mut _;
unsafe {
diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs
index 96ef909ea8f..217e66837bb 100644
--- a/components/script/dom/bindings/trace.rs
+++ b/components/script/dom/bindings/trace.rs
@@ -81,7 +81,7 @@ use media::WindowGLContext;
use metrics::{InteractiveMetrics, InteractiveWindow};
use mime::Mime;
use msg::constellation_msg::{
- BrowsingContextId, HistoryStateId, MessagePortId, MessagePortRouterId, PipelineId,
+ BlobId, BrowsingContextId, HistoryStateId, MessagePortId, MessagePortRouterId, PipelineId,
TopLevelBrowsingContextId,
};
use net_traits::filemanager_thread::RelativePos;
@@ -96,6 +96,7 @@ use profile_traits::mem::ProfilerChan as MemProfilerChan;
use profile_traits::time::ProfilerChan as TimeProfilerChan;
use script_layout_interface::rpc::LayoutRPC;
use script_layout_interface::OpaqueStyleAndLayoutData;
+use script_traits::serializable::BlobImpl;
use script_traits::transferable::MessagePortImpl;
use script_traits::{DocumentActivity, DrawAPaintImageResult};
use script_traits::{MediaSessionActionType, ScriptToConstellationChan, TimerEventId, TimerSource};
@@ -168,6 +169,9 @@ unsafe_no_jsmanaged_fields!(MessagePortId);
unsafe_no_jsmanaged_fields!(RefCell<Option<MessagePortId>>);
unsafe_no_jsmanaged_fields!(MessagePortRouterId);
+unsafe_no_jsmanaged_fields!(BlobId);
+unsafe_no_jsmanaged_fields!(BlobImpl);
+
unsafe_no_jsmanaged_fields!(CSSError);
unsafe_no_jsmanaged_fields!(&'static Encoding);
diff --git a/components/script/dom/blob.rs b/components/script/dom/blob.rs
index db90b2d2bda..2018a12ae88 100644
--- a/components/script/dom/blob.rs
+++ b/components/script/dom/blob.rs
@@ -2,119 +2,53 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
-use crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::BlobBinding;
use crate::dom::bindings::codegen::Bindings::BlobBinding::BlobMethods;
use crate::dom::bindings::codegen::UnionTypes::ArrayBufferOrArrayBufferViewOrBlobOrString;
use crate::dom::bindings::error::{Error, Fallible};
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector};
-use crate::dom::bindings::root::{Dom, DomRoot};
+use crate::dom::bindings::root::DomRoot;
+use crate::dom::bindings::serializable::{Serializable, StorageKey};
use crate::dom::bindings::str::DOMString;
+use crate::dom::bindings::structuredclone::StructuredDataHolder;
use crate::dom::globalscope::GlobalScope;
use dom_struct::dom_struct;
-use net_traits::blob_url_store::{get_blob_origin, BlobBuf};
-use net_traits::filemanager_thread::{FileManagerThreadMsg, ReadFileProgress, RelativePos};
-use net_traits::{CoreResourceMsg, IpcSend};
-use profile_traits::ipc;
-use std::mem;
-use std::ops::Index;
-use std::path::PathBuf;
+use msg::constellation_msg::{BlobId, BlobIndex, PipelineNamespaceId};
+use net_traits::filemanager_thread::RelativePos;
+use script_traits::serializable::BlobImpl;
+use std::collections::HashMap;
+use std::num::NonZeroU32;
use uuid::Uuid;
-/// File-based blob
-#[derive(JSTraceable)]
-pub struct FileBlob {
- id: Uuid,
- name: Option<PathBuf>,
- cache: DomRefCell<Option<Vec<u8>>>,
- size: u64,
-}
-
-/// Different backends of Blob
-#[unrooted_must_root_lint::must_root]
-#[derive(JSTraceable)]
-pub enum BlobImpl {
- /// File-based blob, whose content lives in the net process
- File(FileBlob),
- /// Memory-based blob, whose content lives in the script process
- Memory(Vec<u8>),
- /// Sliced blob, including parent blob reference and
- /// relative positions of current slicing range,
- /// IMPORTANT: The depth of tree is only two, i.e. the parent Blob must be
- /// either File-based or Memory-based
- Sliced(Dom<Blob>, RelativePos),
-}
-
-impl BlobImpl {
- /// Construct memory-backed BlobImpl
- #[allow(unrooted_must_root)]
- pub fn new_from_bytes(bytes: Vec<u8>) -> BlobImpl {
- BlobImpl::Memory(bytes)
- }
-
- /// Construct file-backed BlobImpl from File ID
- pub fn new_from_file(file_id: Uuid, name: PathBuf, size: u64) -> BlobImpl {
- BlobImpl::File(FileBlob {
- id: file_id,
- name: Some(name),
- cache: DomRefCell::new(None),
- size: size,
- })
- }
-}
-
// https://w3c.github.io/FileAPI/#blob
#[dom_struct]
pub struct Blob {
reflector_: Reflector,
- #[ignore_malloc_size_of = "No clear owner"]
- blob_impl: DomRefCell<BlobImpl>,
- /// content-type string
- type_string: String,
+ blob_id: BlobId,
}
impl Blob {
- #[allow(unrooted_must_root)]
- pub fn new(global: &GlobalScope, blob_impl: BlobImpl, typeString: String) -> DomRoot<Blob> {
- let boxed_blob = Box::new(Blob::new_inherited(blob_impl, typeString));
- reflect_dom_object(boxed_blob, global, BlobBinding::Wrap)
+ pub fn new(global: &GlobalScope, blob_impl: BlobImpl) -> DomRoot<Blob> {
+ let dom_blob = reflect_dom_object(
+ Box::new(Blob {
+ reflector_: Reflector::new(),
+ blob_id: blob_impl.blob_id(),
+ }),
+ global,
+ BlobBinding::Wrap,
+ );
+ global.track_blob(&dom_blob, blob_impl);
+ dom_blob
}
#[allow(unrooted_must_root)]
- pub fn new_inherited(blob_impl: BlobImpl, type_string: String) -> Blob {
+ pub fn new_inherited(blob_impl: &BlobImpl) -> Blob {
Blob {
reflector_: Reflector::new(),
- blob_impl: DomRefCell::new(blob_impl),
- // NOTE: Guarding the format correctness here,
- // https://w3c.github.io/FileAPI/#dfn-type
- type_string: normalize_type_string(&type_string),
+ blob_id: blob_impl.blob_id(),
}
}
- #[allow(unrooted_must_root)]
- fn new_sliced(
- parent: &Blob,
- rel_pos: RelativePos,
- relative_content_type: DOMString,
- ) -> DomRoot<Blob> {
- let blob_impl = match *parent.blob_impl.borrow() {
- BlobImpl::File(_) => {
- // Create new parent node
- BlobImpl::Sliced(Dom::from_ref(parent), rel_pos)
- },
- BlobImpl::Memory(_) => {
- // Create new parent node
- BlobImpl::Sliced(Dom::from_ref(parent), rel_pos)
- },
- BlobImpl::Sliced(ref grandparent, ref old_rel_pos) => {
- // Adjust the slicing position, using same parent
- BlobImpl::Sliced(grandparent.clone(), old_rel_pos.slice_inner(&rel_pos))
- },
- };
-
- Blob::new(&parent.global(), blob_impl, relative_content_type.into())
- }
-
// https://w3c.github.io/FileAPI/#constructorBlob
pub fn Constructor(
global: &GlobalScope,
@@ -130,212 +64,107 @@ impl Blob {
},
};
- Ok(Blob::new(
- global,
- BlobImpl::new_from_bytes(bytes),
- blobPropertyBag.type_.to_string(),
- ))
+ let type_string = normalize_type_string(&blobPropertyBag.type_.to_string());
+ let blob_impl = BlobImpl::new_from_bytes(bytes, type_string);
+
+ Ok(Blob::new(global, blob_impl))
}
/// Get a slice to inner data, this might incur synchronous read and caching
pub fn get_bytes(&self) -> Result<Vec<u8>, ()> {
- match *self.blob_impl.borrow() {
- BlobImpl::File(ref f) => {
- let (buffer, is_new_buffer) = match *f.cache.borrow() {
- Some(ref bytes) => (bytes.clone(), false),
- None => {
- let bytes = read_file(&self.global(), f.id.clone())?;
- (bytes, true)
- },
- };
-
- // Cache
- if is_new_buffer {
- *f.cache.borrow_mut() = Some(buffer.clone());
- }
-
- Ok(buffer)
- },
- BlobImpl::Memory(ref s) => Ok(s.clone()),
- BlobImpl::Sliced(ref parent, ref rel_pos) => parent.get_bytes().map(|v| {
- let range = rel_pos.to_abs_range(v.len());
- v.index(range).to_vec()
- }),
- }
+ self.global().get_blob_bytes(&self.blob_id)
}
/// Get a copy of the type_string
pub fn type_string(&self) -> String {
- self.type_string.clone()
+ self.global().get_blob_type_string(&self.blob_id)
}
/// Get a FileID representing the Blob content,
/// used by URL.createObjectURL
pub fn get_blob_url_id(&self) -> Uuid {
- let opt_sliced_parent = match *self.blob_impl.borrow() {
- BlobImpl::Sliced(ref parent, ref rel_pos) => {
- Some((
- parent.promote(/* set_valid is */ false),
- rel_pos.clone(),
- parent.Size(),
- ))
- },
- _ => None,
- };
-
- match opt_sliced_parent {
- Some((parent_id, rel_pos, size)) => {
- self.create_sliced_url_id(&parent_id, &rel_pos, size)
- },
- None => self.promote(/* set_valid is */ true),
- }
+ self.global().get_blob_url_id(&self.blob_id)
}
+}
- /// Promote non-Slice blob:
- /// 1. Memory-based: The bytes in data slice will be transferred to file manager thread.
- /// 2. File-based: If set_valid, then activate the FileID so it can serve as URL
- /// Depending on set_valid, the returned FileID can be part of
- /// valid or invalid Blob URL.
- fn promote(&self, set_valid: bool) -> Uuid {
- let mut bytes = vec![];
- let global_url = self.global().get_url();
-
- match *self.blob_impl.borrow_mut() {
- BlobImpl::Sliced(_, _) => {
- debug!("Sliced can't have a sliced parent");
- // Return dummy id
- return Uuid::new_v4();
- },
- BlobImpl::File(ref f) => {
- if set_valid {
- let origin = get_blob_origin(&global_url);
- let (tx, rx) =
- ipc::channel(self.global().time_profiler_chan().clone()).unwrap();
-
- let msg =
- FileManagerThreadMsg::ActivateBlobURL(f.id.clone(), tx, origin.clone());
- self.send_to_file_manager(msg);
-
- match rx.recv().unwrap() {
- Ok(_) => return f.id.clone(),
- // Return a dummy id on error
- Err(_) => return Uuid::new_v4(),
- }
- } else {
- // no need to activate
- return f.id.clone();
- }
- },
- BlobImpl::Memory(ref mut bytes_in) => mem::swap(bytes_in, &mut bytes),
+impl Serializable for Blob {
+ /// <https://w3c.github.io/FileAPI/#ref-for-serialization-steps>
+ fn serialize(&self, sc_holder: &mut StructuredDataHolder) -> Result<StorageKey, ()> {
+ let blob_impls = match sc_holder {
+ StructuredDataHolder::Write { blobs, .. } => blobs,
+ _ => panic!("Unexpected variant of StructuredDataHolder"),
};
- let origin = get_blob_origin(&global_url);
+ let blob_id = self.blob_id.clone();
- let blob_buf = BlobBuf {
- filename: None,
- type_string: self.type_string.clone(),
- size: bytes.len() as u64,
- bytes: bytes.to_vec(),
- };
-
- let id = Uuid::new_v4();
- let msg = FileManagerThreadMsg::PromoteMemory(id, blob_buf, set_valid, origin.clone());
- self.send_to_file_manager(msg);
-
- *self.blob_impl.borrow_mut() = BlobImpl::File(FileBlob {
- id: id.clone(),
- name: None,
- cache: DomRefCell::new(Some(bytes.to_vec())),
- size: bytes.len() as u64,
- });
- id
- }
+ // 1. Get a clone of the blob impl.
+ let blob_impl = self.global().serialize_blob(&blob_id);
- /// Get a FileID representing sliced parent-blob content
- fn create_sliced_url_id(
- &self,
- parent_id: &Uuid,
- rel_pos: &RelativePos,
- parent_len: u64,
- ) -> Uuid {
- let origin = get_blob_origin(&self.global().get_url());
-
- let (tx, rx) = ipc::channel(self.global().time_profiler_chan().clone()).unwrap();
- let msg = FileManagerThreadMsg::AddSlicedURLEntry(
- parent_id.clone(),
- rel_pos.clone(),
- tx,
- origin.clone(),
- );
- self.send_to_file_manager(msg);
- match rx.recv().expect("File manager thread is down") {
- Ok(new_id) => {
- *self.blob_impl.borrow_mut() = BlobImpl::File(FileBlob {
- id: new_id.clone(),
- name: None,
- cache: DomRefCell::new(None),
- size: rel_pos.to_abs_range(parent_len as usize).len() as u64,
- });
-
- // Return the indirect id reference
- new_id
- },
- Err(_) => {
- // Return dummy id
- Uuid::new_v4()
- },
- }
- }
+ // We clone the data, but the clone gets its own Id.
+ let new_blob_id = blob_impl.blob_id();
- /// Cleanups at the time of destruction/closing
- fn clean_up_file_resource(&self) {
- if let BlobImpl::File(ref f) = *self.blob_impl.borrow() {
- let origin = get_blob_origin(&self.global().get_url());
+ // 2. Store the object at a given key.
+ let blobs = blob_impls.get_or_insert_with(|| HashMap::new());
+ blobs.insert(new_blob_id.clone(), blob_impl);
- let (tx, rx) = ipc::channel(self.global().time_profiler_chan().clone()).unwrap();
+ let PipelineNamespaceId(name_space) = new_blob_id.namespace_id;
+ let BlobIndex(index) = new_blob_id.index;
+ let index = index.get();
- let msg = FileManagerThreadMsg::DecRef(f.id.clone(), origin, tx);
- self.send_to_file_manager(msg);
- let _ = rx.recv();
- }
- }
+ let name_space = name_space.to_ne_bytes();
+ let index = index.to_ne_bytes();
- fn send_to_file_manager(&self, msg: FileManagerThreadMsg) {
- let global = self.global();
- let resource_threads = global.resource_threads();
- let _ = resource_threads.send(CoreResourceMsg::ToFileManager(msg));
- }
-}
+ let storage_key = StorageKey {
+ index: u32::from_ne_bytes(index),
+ name_space: u32::from_ne_bytes(name_space),
+ };
-impl Drop for Blob {
- fn drop(&mut self) {
- self.clean_up_file_resource();
- }
-}
+ // 3. Return the storage key.
+ Ok(storage_key)
+ }
+
+ /// <https://w3c.github.io/FileAPI/#ref-for-deserialization-steps>
+ fn deserialize(
+ owner: &DomRoot<GlobalScope>,
+ sc_holder: &mut StructuredDataHolder,
+ storage_key: StorageKey,
+ ) -> Result<(), ()> {
+ // 1. Re-build the key for the storage location
+ // of the serialized object.
+ let namespace_id = PipelineNamespaceId(storage_key.name_space.clone());
+ let index = BlobIndex(
+ NonZeroU32::new(storage_key.index.clone()).expect("Deserialized blob index is zero"),
+ );
-fn read_file(global: &GlobalScope, id: Uuid) -> Result<Vec<u8>, ()> {
- let resource_threads = global.resource_threads();
- let (chan, recv) = ipc::channel(global.time_profiler_chan().clone()).map_err(|_| ())?;
- let origin = get_blob_origin(&global.get_url());
- let check_url_validity = false;
- let msg = FileManagerThreadMsg::ReadFile(chan, id, check_url_validity, origin);
- let _ = resource_threads.send(CoreResourceMsg::ToFileManager(msg));
+ let id = BlobId {
+ namespace_id,
+ index,
+ };
- let mut bytes = vec![];
+ let (blobs, blob_impls) = match sc_holder {
+ StructuredDataHolder::Read {
+ blobs, blob_impls, ..
+ } => (blobs, blob_impls),
+ _ => panic!("Unexpected variant of StructuredDataHolder"),
+ };
- loop {
- match recv.recv().unwrap() {
- Ok(ReadFileProgress::Meta(mut blob_buf)) => {
- bytes.append(&mut blob_buf.bytes);
- },
- Ok(ReadFileProgress::Partial(mut bytes_in)) => {
- bytes.append(&mut bytes_in);
- },
- Ok(ReadFileProgress::EOF) => {
- return Ok(bytes);
- },
- Err(_) => return Err(()),
+ // 2. Get the transferred object from its storage, using the key.
+ let blob_impls_map = blob_impls
+ .as_mut()
+ .expect("The SC holder does not have any blob impls");
+ let blob_impl = blob_impls_map
+ .remove(&id)
+ .expect("No blob to be deserialized found.");
+ if blob_impls_map.is_empty() {
+ *blob_impls = None;
}
+
+ let deserialized_blob = Blob::new(&**owner, blob_impl);
+
+ let blobs = blobs.get_or_insert_with(|| HashMap::new());
+ blobs.insert(storage_key, deserialized_blob);
+
+ Ok(())
}
}
@@ -372,18 +201,12 @@ pub fn blob_parts_to_bytes(
impl BlobMethods for Blob {
// https://w3c.github.io/FileAPI/#dfn-size
fn Size(&self) -> u64 {
- match *self.blob_impl.borrow() {
- BlobImpl::File(ref f) => f.size,
- BlobImpl::Memory(ref v) => v.len() as u64,
- BlobImpl::Sliced(ref parent, ref rel_pos) => {
- rel_pos.to_abs_range(parent.Size() as usize).len() as u64
- },
- }
+ self.global().get_blob_size(&self.blob_id)
}
// https://w3c.github.io/FileAPI/#dfn-type
fn Type(&self) -> DOMString {
- DOMString::from(self.type_string.clone())
+ DOMString::from(self.type_string())
}
// https://w3c.github.io/FileAPI/#slice-method-algo
@@ -393,8 +216,11 @@ impl BlobMethods for Blob {
end: Option<i64>,
content_type: Option<DOMString>,
) -> DomRoot<Blob> {
+ let type_string =
+ normalize_type_string(&content_type.unwrap_or(DOMString::from("")).to_string());
let rel_pos = RelativePos::from_opts(start, end);
- Blob::new_sliced(self, rel_pos, content_type.unwrap_or(DOMString::from("")))
+ let blob_impl = BlobImpl::new_sliced(rel_pos, self.blob_id.clone(), type_string);
+ Blob::new(&*self.global(), blob_impl)
}
}
@@ -403,7 +229,7 @@ impl BlobMethods for Blob {
/// XXX: We will relax the restriction here,
/// since the spec has some problem over this part.
/// see https://github.com/w3c/FileAPI/issues/43
-fn normalize_type_string(s: &str) -> String {
+pub fn normalize_type_string(s: &str) -> String {
if is_ascii_printable(s) {
let s_lower = s.to_ascii_lowercase();
// match s_lower.parse() as Result<Mime, ()> {
diff --git a/components/script/dom/dedicatedworkerglobalscope.rs b/components/script/dom/dedicatedworkerglobalscope.rs
index e9579565a44..567d4fa6861 100644
--- a/components/script/dom/dedicatedworkerglobalscope.rs
+++ b/components/script/dom/dedicatedworkerglobalscope.rs
@@ -549,7 +549,7 @@ impl DedicatedWorkerGlobalScope {
Some(pipeline_id),
TaskSourceName::DOMManipulation,
))
- .unwrap();
+ .expect("Sending to parent failed");
Ok(())
}
}
diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs
index 62bce543695..94c9c8603c4 100644
--- a/components/script/dom/document.rs
+++ b/components/script/dom/document.rs
@@ -1916,6 +1916,9 @@ impl Document {
let msg = ScriptMsg::DiscardDocument;
let _ = global_scope.script_to_constellation_chan().send(msg);
}
+ // https://w3c.github.io/FileAPI/#lifeTime
+ global_scope.clean_up_all_file_resources();
+
// Step 15, End
self.decr_ignore_opens_during_unload_counter();
}
diff --git a/components/script/dom/file.rs b/components/script/dom/file.rs
index 87ae3336fec..ed4753a58b3 100644
--- a/components/script/dom/file.rs
+++ b/components/script/dom/file.rs
@@ -10,11 +10,12 @@ use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::reflector::reflect_dom_object;
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::str::DOMString;
-use crate::dom::blob::{blob_parts_to_bytes, Blob, BlobImpl};
+use crate::dom::blob::{blob_parts_to_bytes, normalize_type_string, Blob};
use crate::dom::globalscope::GlobalScope;
use crate::dom::window::Window;
use dom_struct::dom_struct;
use net_traits::filemanager_thread::SelectedFile;
+use script_traits::serializable::BlobImpl;
#[dom_struct]
pub struct File {
@@ -25,14 +26,9 @@ pub struct File {
impl File {
#[allow(unrooted_must_root)]
- fn new_inherited(
- blob_impl: BlobImpl,
- name: DOMString,
- modified: Option<i64>,
- type_string: &str,
- ) -> File {
+ fn new_inherited(blob_impl: &BlobImpl, name: DOMString, modified: Option<i64>) -> File {
File {
- blob: Blob::new_inherited(blob_impl, type_string.to_owned()),
+ blob: Blob::new_inherited(blob_impl),
name: name,
// https://w3c.github.io/FileAPI/#dfn-lastModified
modified: match modified {
@@ -51,13 +47,14 @@ impl File {
blob_impl: BlobImpl,
name: DOMString,
modified: Option<i64>,
- typeString: &str,
) -> DomRoot<File> {
- reflect_dom_object(
- Box::new(File::new_inherited(blob_impl, name, modified, typeString)),
+ let file = reflect_dom_object(
+ Box::new(File::new_inherited(&blob_impl, name, modified)),
global,
FileBinding::Wrap,
- )
+ );
+ global.track_file(&file, blob_impl);
+ file
}
// Construct from selected file message from file manager thread
@@ -71,10 +68,14 @@ impl File {
File::new(
window.upcast(),
- BlobImpl::new_from_file(selected.id, selected.filename, selected.size),
+ BlobImpl::new_from_file(
+ selected.id,
+ selected.filename,
+ selected.size,
+ normalize_type_string(&selected.type_string.to_string()),
+ ),
name,
Some(selected.modified as i64),
- &selected.type_string,
)
}
@@ -91,18 +92,17 @@ impl File {
};
let ref blobPropertyBag = filePropertyBag.parent;
- let ref typeString = blobPropertyBag.type_;
let modified = filePropertyBag.lastModified;
// NOTE: Following behaviour might be removed in future,
// see https://github.com/w3c/FileAPI/issues/41
let replaced_filename = DOMString::from_string(filename.replace("/", ":"));
+ let type_string = normalize_type_string(&blobPropertyBag.type_.to_string());
Ok(File::new(
global,
- BlobImpl::new_from_bytes(bytes),
+ BlobImpl::new_from_bytes(bytes, type_string),
replaced_filename,
modified,
- typeString,
))
}
diff --git a/components/script/dom/formdata.rs b/components/script/dom/formdata.rs
index d0572e9b0e1..b8503a4f0dc 100644
--- a/components/script/dom/formdata.rs
+++ b/components/script/dom/formdata.rs
@@ -12,12 +12,13 @@ use crate::dom::bindings::iterable::Iterable;
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector};
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::str::{DOMString, USVString};
-use crate::dom::blob::{Blob, BlobImpl};
+use crate::dom::blob::Blob;
use crate::dom::file::File;
use crate::dom::globalscope::GlobalScope;
use crate::dom::htmlformelement::{FormDatum, FormDatumValue, HTMLFormElement};
use dom_struct::dom_struct;
use html5ever::LocalName;
+use script_traits::serializable::BlobImpl;
#[dom_struct]
pub struct FormData {
@@ -195,10 +196,9 @@ impl FormData {
File::new(
&self.global(),
- BlobImpl::new_from_bytes(bytes),
+ BlobImpl::new_from_bytes(bytes, blob.type_string()),
name,
None,
- &blob.type_string(),
)
}
diff --git a/components/script/dom/globalscope.rs b/components/script/dom/globalscope.rs
index 1e3371968e9..f0748433e64 100644
--- a/components/script/dom/globalscope.rs
+++ b/components/script/dom/globalscope.rs
@@ -16,12 +16,14 @@ use crate::dom::bindings::settings_stack::{entry_global, incumbent_global, AutoE
use crate::dom::bindings::str::DOMString;
use crate::dom::bindings::structuredclone;
use crate::dom::bindings::weakref::{DOMTracker, WeakRef};
+use crate::dom::blob::Blob;
use crate::dom::crypto::Crypto;
use crate::dom::dedicatedworkerglobalscope::DedicatedWorkerGlobalScope;
use crate::dom::errorevent::ErrorEvent;
use crate::dom::event::{Event, EventBubbles, EventCancelable, EventStatus};
use crate::dom::eventsource::EventSource;
use crate::dom::eventtarget::EventTarget;
+use crate::dom::file::File;
use crate::dom::messageevent::MessageEvent;
use crate::dom::messageport::MessagePort;
use crate::dom::paintworkletglobalscope::PaintWorkletGlobalScope;
@@ -61,10 +63,13 @@ use js::rust::wrappers::EvaluateUtf8;
use js::rust::{get_object_class, CompileOptionsWrapper, ParentRuntime, Runtime};
use js::rust::{HandleValue, MutableHandleValue};
use js::{JSCLASS_IS_DOMJSCLASS, JSCLASS_IS_GLOBAL};
-use msg::constellation_msg::{MessagePortId, MessagePortRouterId, PipelineId};
+use msg::constellation_msg::{BlobId, MessagePortId, MessagePortRouterId, PipelineId};
+use net_traits::blob_url_store::{get_blob_origin, BlobBuf};
+use net_traits::filemanager_thread::{FileManagerThreadMsg, ReadFileProgress, RelativePos};
use net_traits::image_cache::ImageCache;
-use net_traits::{CoreResourceThread, IpcSend, ResourceThreads};
-use profile_traits::{mem as profile_mem, time as profile_time};
+use net_traits::{CoreResourceMsg, CoreResourceThread, IpcSend, ResourceThreads};
+use profile_traits::{ipc as profile_ipc, mem as profile_mem, time as profile_time};
+use script_traits::serializable::{BlobData, BlobImpl, FileBlob};
use script_traits::transferable::MessagePortImpl;
use script_traits::{
MessagePortMsg, MsDuration, PortMessageTask, ScriptMsg, ScriptToConstellationChan, TimerEvent,
@@ -76,10 +81,13 @@ use std::cell::Cell;
use std::collections::hash_map::Entry;
use std::collections::{HashMap, VecDeque};
use std::ffi::CString;
+use std::mem;
+use std::ops::Index;
use std::rc::Rc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use time::{get_time, Timespec};
+use uuid::Uuid;
#[derive(JSTraceable)]
pub struct AutoCloseWorker(Arc<AtomicBool>);
@@ -98,6 +106,9 @@ pub struct GlobalScope {
/// The message-port router id for this global, if it is managing ports.
message_port_state: DomRefCell<MessagePortState>,
+ /// The blobs managed by this global, if any.
+ blob_state: DomRefCell<BlobState>,
+
/// Pipeline id associated with this global.
pipeline_id: PipelineId,
@@ -200,6 +211,36 @@ struct TimerListener {
context: Trusted<GlobalScope>,
}
+#[derive(JSTraceable, MallocSizeOf)]
+/// A holder of a weak reference for a DOM blob or file.
+pub enum BlobTracker {
+ /// A weak ref to a DOM file.
+ File(WeakRef<File>),
+ /// A weak ref to a DOM blob.
+ Blob(WeakRef<Blob>),
+}
+
+#[derive(JSTraceable, MallocSizeOf)]
+/// The info pertaining to a blob managed by this global.
+pub struct BlobInfo {
+ /// The weak ref to the corresponding DOM object.
+ tracker: BlobTracker,
+ /// The data and logic backing the DOM object.
+ blob_impl: BlobImpl,
+ /// Whether this blob has an outstanding URL,
+ /// <https://w3c.github.io/FileAPI/#url>.
+ has_url: bool,
+}
+
+/// State representing whether this global is currently managing blobs.
+#[derive(JSTraceable, MallocSizeOf)]
+pub enum BlobState {
+ /// A map of managed blobs.
+ Managed(HashMap<BlobId, BlobInfo>),
+ /// This global is not managing any blobs at this time.
+ UnManaged,
+}
+
/// Data representing a message-port managed by this global.
#[derive(JSTraceable, MallocSizeOf)]
pub enum ManagedMessagePort {
@@ -344,6 +385,7 @@ impl GlobalScope {
) -> Self {
Self {
message_port_state: DomRefCell::new(MessagePortState::UnManaged),
+ blob_state: DomRefCell::new(BlobState::UnManaged),
eventtarget: EventTarget::new_inherited(),
crypto: Default::default(),
pipeline_id,
@@ -443,6 +485,12 @@ impl GlobalScope {
}
}
+ /// Clean-up DOM related resources
+ pub fn perform_a_dom_garbage_collection_checkpoint(&self) {
+ self.perform_a_message_port_garbage_collection_checkpoint();
+ self.perform_a_blob_garbage_collection_checkpoint();
+ }
+
/// Update our state to un-managed,
/// and tell the constellation to drop the sender to our message-port router.
pub fn remove_message_ports_router(&self) {
@@ -812,6 +860,400 @@ impl GlobalScope {
}
}
+ /// <https://html.spec.whatwg.org/multipage/#serialization-steps>
+ /// defined at <https://w3c.github.io/FileAPI/#blob-section>.
+ /// Get the snapshot state and underlying bytes of the blob.
+ pub fn serialize_blob(&self, blob_id: &BlobId) -> BlobImpl {
+ // Note: we combine the snapshot state and underlying bytes into one call,
+ // which seems spec compliant.
+ // See https://w3c.github.io/FileAPI/#snapshot-state
+ let bytes = self
+ .get_blob_bytes(blob_id)
+ .expect("Could not read bytes from blob as part of serialization steps.");
+ let type_string = self.get_blob_type_string(blob_id);
+
+ // Note: the new BlobImpl is a clone, but with it's own BlobId.
+ BlobImpl::new_from_bytes(bytes, type_string)
+ }
+
+ fn track_blob_info(&self, blob_info: BlobInfo, blob_id: BlobId) {
+ let mut blob_state = self.blob_state.borrow_mut();
+
+ match &mut *blob_state {
+ BlobState::UnManaged => {
+ let mut blobs_map = HashMap::new();
+ blobs_map.insert(blob_id, blob_info);
+ *blob_state = BlobState::Managed(blobs_map);
+ },
+ BlobState::Managed(blobs_map) => {
+ blobs_map.insert(blob_id, blob_info);
+ },
+ }
+ }
+
+ /// Start tracking a blob
+ pub fn track_blob(&self, dom_blob: &DomRoot<Blob>, blob_impl: BlobImpl) {
+ let blob_id = blob_impl.blob_id();
+
+ let blob_info = BlobInfo {
+ blob_impl,
+ tracker: BlobTracker::Blob(WeakRef::new(dom_blob)),
+ has_url: false,
+ };
+
+ self.track_blob_info(blob_info, blob_id);
+ }
+
+ /// Start tracking a file
+ pub fn track_file(&self, file: &DomRoot<File>, blob_impl: BlobImpl) {
+ let blob_id = blob_impl.blob_id();
+
+ let blob_info = BlobInfo {
+ blob_impl,
+ tracker: BlobTracker::File(WeakRef::new(file)),
+ has_url: false,
+ };
+
+ self.track_blob_info(blob_info, blob_id);
+ }
+
+ /// Clean-up any file or blob that is unreachable from script,
+ /// unless it has an oustanding blob url.
+ /// <https://w3c.github.io/FileAPI/#lifeTime>
+ fn perform_a_blob_garbage_collection_checkpoint(&self) {
+ let mut blob_state = self.blob_state.borrow_mut();
+ if let BlobState::Managed(blobs_map) = &mut *blob_state {
+ blobs_map.retain(|_id, blob_info| {
+ let garbage_collected = match &blob_info.tracker {
+ BlobTracker::File(weak) => weak.root().is_none(),
+ BlobTracker::Blob(weak) => weak.root().is_none(),
+ };
+ if garbage_collected && !blob_info.has_url {
+ if let BlobData::File(ref f) = blob_info.blob_impl.blob_data() {
+ self.decrement_file_ref(f.get_id());
+ }
+ false
+ } else {
+ true
+ }
+ });
+ if blobs_map.is_empty() {
+ *blob_state = BlobState::UnManaged;
+ }
+ }
+ }
+
+ /// Clean-up all file related resources on document unload.
+ /// <https://w3c.github.io/FileAPI/#lifeTime>
+ pub fn clean_up_all_file_resources(&self) {
+ let mut blob_state = self.blob_state.borrow_mut();
+ if let BlobState::Managed(blobs_map) = &mut *blob_state {
+ blobs_map.drain().for_each(|(_id, blob_info)| {
+ if let BlobData::File(ref f) = blob_info.blob_impl.blob_data() {
+ self.decrement_file_ref(f.get_id());
+ }
+ });
+ }
+ *blob_state = BlobState::UnManaged;
+ }
+
+ fn decrement_file_ref(&self, id: Uuid) {
+ let origin = get_blob_origin(&self.get_url());
+
+ let (tx, rx) = profile_ipc::channel(self.time_profiler_chan().clone()).unwrap();
+
+ let msg = FileManagerThreadMsg::DecRef(id, origin, tx);
+ self.send_to_file_manager(msg);
+ let _ = rx.recv();
+ }
+
+ /// Get a slice to the inner data of a Blob,
+ /// In the case of a File-backed blob, this might incur synchronous read and caching.
+ pub fn get_blob_bytes(&self, blob_id: &BlobId) -> Result<Vec<u8>, ()> {
+ let parent = {
+ let blob_state = self.blob_state.borrow();
+ if let BlobState::Managed(blobs_map) = &*blob_state {
+ let blob_info = blobs_map
+ .get(blob_id)
+ .expect("get_blob_bytes for an unknown blob.");
+ match blob_info.blob_impl.blob_data() {
+ BlobData::Sliced(ref parent, ref rel_pos) => {
+ Some((parent.clone(), rel_pos.clone()))
+ },
+ _ => None,
+ }
+ } else {
+ panic!("get_blob_bytes called on a global not managing any blobs.");
+ }
+ };
+
+ match parent {
+ Some((parent_id, rel_pos)) => self.get_blob_bytes_non_sliced(&parent_id).map(|v| {
+ let range = rel_pos.to_abs_range(v.len());
+ v.index(range).to_vec()
+ }),
+ None => self.get_blob_bytes_non_sliced(blob_id),
+ }
+ }
+
+ /// Get bytes from a non-sliced blob
+ fn get_blob_bytes_non_sliced(&self, blob_id: &BlobId) -> Result<Vec<u8>, ()> {
+ let blob_state = self.blob_state.borrow();
+ if let BlobState::Managed(blobs_map) = &*blob_state {
+ let blob_info = blobs_map
+ .get(blob_id)
+ .expect("get_blob_bytes_non_sliced called for a unknown blob.");
+ match blob_info.blob_impl.blob_data() {
+ BlobData::File(ref f) => {
+ let (buffer, is_new_buffer) = match f.get_cache() {
+ Some(bytes) => (bytes, false),
+ None => {
+ let bytes = self.read_file(f.get_id())?;
+ (bytes, true)
+ },
+ };
+
+ // Cache
+ if is_new_buffer {
+ f.cache_bytes(buffer.clone());
+ }
+
+ Ok(buffer)
+ },
+ BlobData::Memory(ref s) => Ok(s.clone()),
+ BlobData::Sliced(_, _) => panic!("This blob doesn't have a parent."),
+ }
+ } else {
+ panic!("get_blob_bytes_non_sliced called on a global not managing any blobs.");
+ }
+ }
+
+ /// Get a copy of the type_string of a blob.
+ pub fn get_blob_type_string(&self, blob_id: &BlobId) -> String {
+ let blob_state = self.blob_state.borrow();
+ if let BlobState::Managed(blobs_map) = &*blob_state {
+ let blob_info = blobs_map
+ .get(blob_id)
+ .expect("get_blob_type_string called for a unknown blob.");
+ blob_info.blob_impl.type_string()
+ } else {
+ panic!("get_blob_type_string called on a global not managing any blobs.");
+ }
+ }
+
+ /// https://w3c.github.io/FileAPI/#dfn-size
+ pub fn get_blob_size(&self, blob_id: &BlobId) -> u64 {
+ let blob_state = self.blob_state.borrow();
+ if let BlobState::Managed(blobs_map) = &*blob_state {
+ let parent = {
+ let blob_info = blobs_map
+ .get(blob_id)
+ .expect("get_blob_size called for a unknown blob.");
+ match blob_info.blob_impl.blob_data() {
+ BlobData::Sliced(ref parent, ref rel_pos) => {
+ Some((parent.clone(), rel_pos.clone()))
+ },
+ _ => None,
+ }
+ };
+ match parent {
+ Some((parent_id, rel_pos)) => {
+ let parent_info = blobs_map
+ .get(&parent_id)
+ .expect("Parent of blob whose size is unknown.");
+ let parent_size = match parent_info.blob_impl.blob_data() {
+ BlobData::File(ref f) => f.get_size(),
+ BlobData::Memory(ref v) => v.len() as u64,
+ BlobData::Sliced(_, _) => panic!("Blob ancestry should be only one level."),
+ };
+ rel_pos.to_abs_range(parent_size as usize).len() as u64
+ },
+ None => {
+ let blob_info = blobs_map.get(blob_id).expect("Blob whose size is unknown.");
+ match blob_info.blob_impl.blob_data() {
+ BlobData::File(ref f) => f.get_size(),
+ BlobData::Memory(ref v) => v.len() as u64,
+ BlobData::Sliced(_, _) => panic!(
+ "It was previously checked that this blob does not have a parent."
+ ),
+ }
+ },
+ }
+ } else {
+ panic!("get_blob_size called on a global not managing any blobs.");
+ }
+ }
+
+ pub fn get_blob_url_id(&self, blob_id: &BlobId) -> Uuid {
+ let mut blob_state = self.blob_state.borrow_mut();
+ if let BlobState::Managed(blobs_map) = &mut *blob_state {
+ let parent = {
+ let blob_info = blobs_map
+ .get_mut(blob_id)
+ .expect("get_blob_url_id called for a unknown blob.");
+
+ // Keep track of blobs with outstanding URLs.
+ blob_info.has_url = true;
+
+ match blob_info.blob_impl.blob_data() {
+ BlobData::Sliced(ref parent, ref rel_pos) => {
+ Some((parent.clone(), rel_pos.clone()))
+ },
+ _ => None,
+ }
+ };
+ match parent {
+ Some((parent_id, rel_pos)) => {
+ let parent_file_id = {
+ let parent_info = blobs_map
+ .get_mut(&parent_id)
+ .expect("Parent of blob whose url is requested is unknown.");
+ self.promote(parent_info, /* set_valid is */ false)
+ };
+ let parent_size = self.get_blob_size(&parent_id);
+ let blob_info = blobs_map
+ .get_mut(blob_id)
+ .expect("Blob whose url is requested is unknown.");
+ self.create_sliced_url_id(blob_info, &parent_file_id, &rel_pos, parent_size)
+ },
+ None => {
+ let blob_info = blobs_map
+ .get_mut(blob_id)
+ .expect("Blob whose url is requested is unknown.");
+ self.promote(blob_info, /* set_valid is */ true)
+ },
+ }
+ } else {
+ panic!("get_blob_url_id called on a global not managing any blobs.");
+ }
+ }
+
+ /// Get a FileID representing sliced parent-blob content
+ fn create_sliced_url_id(
+ &self,
+ blob_info: &mut BlobInfo,
+ parent_file_id: &Uuid,
+ rel_pos: &RelativePos,
+ parent_len: u64,
+ ) -> Uuid {
+ let origin = get_blob_origin(&self.get_url());
+
+ let (tx, rx) = profile_ipc::channel(self.time_profiler_chan().clone()).unwrap();
+ let msg = FileManagerThreadMsg::AddSlicedURLEntry(
+ parent_file_id.clone(),
+ rel_pos.clone(),
+ tx,
+ origin.clone(),
+ );
+ self.send_to_file_manager(msg);
+ match rx.recv().expect("File manager thread is down.") {
+ Ok(new_id) => {
+ *blob_info.blob_impl.blob_data_mut() = BlobData::File(FileBlob::new(
+ new_id.clone(),
+ None,
+ None,
+ rel_pos.to_abs_range(parent_len as usize).len() as u64,
+ ));
+
+ // Return the indirect id reference
+ new_id
+ },
+ Err(_) => {
+ // Return dummy id
+ Uuid::new_v4()
+ },
+ }
+ }
+
+ /// Promote non-Slice blob:
+ /// 1. Memory-based: The bytes in data slice will be transferred to file manager thread.
+ /// 2. File-based: If set_valid, then activate the FileID so it can serve as URL
+ /// Depending on set_valid, the returned FileID can be part of
+ /// valid or invalid Blob URL.
+ pub fn promote(&self, blob_info: &mut BlobInfo, set_valid: bool) -> Uuid {
+ let mut bytes = vec![];
+ let global_url = self.get_url();
+
+ match blob_info.blob_impl.blob_data_mut() {
+ BlobData::Sliced(_, _) => {
+ panic!("Sliced blobs should use create_sliced_url_id instead of promote.");
+ },
+ BlobData::File(ref f) => {
+ if set_valid {
+ let origin = get_blob_origin(&global_url);
+ let (tx, rx) = profile_ipc::channel(self.time_profiler_chan().clone()).unwrap();
+
+ let msg = FileManagerThreadMsg::ActivateBlobURL(f.get_id(), tx, origin.clone());
+ self.send_to_file_manager(msg);
+
+ match rx.recv().unwrap() {
+ Ok(_) => return f.get_id(),
+ // Return a dummy id on error
+ Err(_) => return Uuid::new_v4(),
+ }
+ } else {
+ // no need to activate
+ return f.get_id();
+ }
+ },
+ BlobData::Memory(ref mut bytes_in) => mem::swap(bytes_in, &mut bytes),
+ };
+
+ let origin = get_blob_origin(&global_url);
+
+ let blob_buf = BlobBuf {
+ filename: None,
+ type_string: blob_info.blob_impl.type_string(),
+ size: bytes.len() as u64,
+ bytes: bytes.to_vec(),
+ };
+
+ let id = Uuid::new_v4();
+ let msg = FileManagerThreadMsg::PromoteMemory(id, blob_buf, set_valid, origin.clone());
+ self.send_to_file_manager(msg);
+
+ *blob_info.blob_impl.blob_data_mut() = BlobData::File(FileBlob::new(
+ id.clone(),
+ None,
+ Some(bytes.to_vec()),
+ bytes.len() as u64,
+ ));
+
+ id
+ }
+
+ fn send_to_file_manager(&self, msg: FileManagerThreadMsg) {
+ let resource_threads = self.resource_threads();
+ let _ = resource_threads.send(CoreResourceMsg::ToFileManager(msg));
+ }
+
+ fn read_file(&self, id: Uuid) -> Result<Vec<u8>, ()> {
+ let resource_threads = self.resource_threads();
+ let (chan, recv) =
+ profile_ipc::channel(self.time_profiler_chan().clone()).map_err(|_| ())?;
+ let origin = get_blob_origin(&self.get_url());
+ let check_url_validity = false;
+ let msg = FileManagerThreadMsg::ReadFile(chan, id, check_url_validity, origin);
+ let _ = resource_threads.send(CoreResourceMsg::ToFileManager(msg));
+
+ let mut bytes = vec![];
+
+ loop {
+ match recv.recv().unwrap() {
+ Ok(ReadFileProgress::Meta(mut blob_buf)) => {
+ bytes.append(&mut blob_buf.bytes);
+ },
+ Ok(ReadFileProgress::Partial(mut bytes_in)) => {
+ bytes.append(&mut bytes_in);
+ },
+ Ok(ReadFileProgress::EOF) => {
+ return Ok(bytes);
+ },
+ Err(_) => return Err(()),
+ }
+ }
+ }
+
pub fn track_worker(&self, closing_worker: Arc<AtomicBool>) {
self.list_auto_close_worker
.borrow_mut()
diff --git a/components/script/dom/history.rs b/components/script/dom/history.rs
index c02cdb42472..ef7a6fbe916 100644
--- a/components/script/dom/history.rs
+++ b/components/script/dom/history.rs
@@ -119,6 +119,7 @@ impl History {
let data = StructuredSerializedData {
serialized: data,
ports: None,
+ blobs: None,
};
let global_scope = self.window.upcast::<GlobalScope>();
rooted!(in(*global_scope.get_cx()) let mut state = UndefinedValue());
diff --git a/components/script/dom/messageport.rs b/components/script/dom/messageport.rs
index 3198df50923..97df51afa04 100644
--- a/components/script/dom/messageport.rs
+++ b/components/script/dom/messageport.rs
@@ -162,7 +162,7 @@ impl Transferable for MessagePort {
}
let port_impls = match sc_holder {
- StructuredDataHolder::Write(port_impls) => port_impls,
+ StructuredDataHolder::Write { ports, .. } => ports,
_ => panic!("Unexpected variant of StructuredDataHolder"),
};
diff --git a/components/script/dom/testbinding.rs b/components/script/dom/testbinding.rs
index 31972afe948..475f432417f 100644
--- a/components/script/dom/testbinding.rs
+++ b/components/script/dom/testbinding.rs
@@ -44,7 +44,7 @@ use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::str::{ByteString, DOMString, USVString};
use crate::dom::bindings::trace::RootedTraceableBox;
use crate::dom::bindings::weakref::MutableWeakRef;
-use crate::dom::blob::{Blob, BlobImpl};
+use crate::dom::blob::Blob;
use crate::dom::globalscope::GlobalScope;
use crate::dom::promise::Promise;
use crate::dom::promisenativehandler::{Callback, PromiseNativeHandler};
@@ -58,6 +58,7 @@ use js::jsval::{JSVal, NullValue};
use js::rust::CustomAutoRooterGuard;
use js::rust::{HandleObject, HandleValue};
use js::typedarray;
+use script_traits::serializable::BlobImpl;
use script_traits::MsDuration;
use servo_config::prefs;
use std::borrow::ToOwned;
@@ -174,8 +175,7 @@ impl TestBindingMethods for TestBinding {
fn InterfaceAttribute(&self) -> DomRoot<Blob> {
Blob::new(
&self.global(),
- BlobImpl::new_from_bytes(vec![]),
- "".to_owned(),
+ BlobImpl::new_from_bytes(vec![], "".to_owned()),
)
}
fn SetInterfaceAttribute(&self, _: &Blob) {}
@@ -320,8 +320,7 @@ impl TestBindingMethods for TestBinding {
fn GetInterfaceAttributeNullable(&self) -> Option<DomRoot<Blob>> {
Some(Blob::new(
&self.global(),
- BlobImpl::new_from_bytes(vec![]),
- "".to_owned(),
+ BlobImpl::new_from_bytes(vec![], "".to_owned()),
))
}
fn SetInterfaceAttributeNullable(&self, _: Option<&Blob>) {}
@@ -415,8 +414,7 @@ impl TestBindingMethods for TestBinding {
fn ReceiveInterface(&self) -> DomRoot<Blob> {
Blob::new(
&self.global(),
- BlobImpl::new_from_bytes(vec![]),
- "".to_owned(),
+ BlobImpl::new_from_bytes(vec![], "".to_owned()),
)
}
fn ReceiveAny(&self, _: SafeJSContext) -> JSVal {
@@ -464,8 +462,7 @@ impl TestBindingMethods for TestBinding {
fn ReceiveInterfaceSequence(&self) -> Vec<DomRoot<Blob>> {
vec![Blob::new(
&self.global(),
- BlobImpl::new_from_bytes(vec![]),
- "".to_owned(),
+ BlobImpl::new_from_bytes(vec![], "".to_owned()),
)]
}
fn ReceiveUnionIdentity(
@@ -530,8 +527,7 @@ impl TestBindingMethods for TestBinding {
fn ReceiveNullableInterface(&self) -> Option<DomRoot<Blob>> {
Some(Blob::new(
&self.global(),
- BlobImpl::new_from_bytes(vec![]),
- "".to_owned(),
+ BlobImpl::new_from_bytes(vec![], "".to_owned()),
))
}
fn ReceiveNullableObject(&self, cx: SafeJSContext) -> Option<NonNull<JSObject>> {
diff --git a/components/script/dom/websocket.rs b/components/script/dom/websocket.rs
index fcdd63e2594..c931437bd4f 100644
--- a/components/script/dom/websocket.rs
+++ b/components/script/dom/websocket.rs
@@ -14,7 +14,7 @@ use crate::dom::bindings::refcounted::Trusted;
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject};
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::str::{is_token, DOMString, USVString};
-use crate::dom::blob::{Blob, BlobImpl};
+use crate::dom::blob::Blob;
use crate::dom::closeevent::CloseEvent;
use crate::dom::event::{Event, EventBubbles, EventCancelable};
use crate::dom::eventtarget::EventTarget;
@@ -37,6 +37,7 @@ use net_traits::MessageData;
use net_traits::{CoreResourceMsg, FetchChannels};
use net_traits::{WebSocketDomAction, WebSocketNetworkEvent};
use profile_traits::ipc as ProfiledIpc;
+use script_traits::serializable::BlobImpl;
use servo_url::{ImmutableOrigin, ServoUrl};
use std::borrow::ToOwned;
use std::cell::Cell;
@@ -576,7 +577,7 @@ impl TaskOnce for MessageReceivedTask {
MessageData::Binary(data) => match ws.binary_type.get() {
BinaryType::Blob => {
let blob =
- Blob::new(&global, BlobImpl::new_from_bytes(data), "".to_owned());
+ Blob::new(&global, BlobImpl::new_from_bytes(data, "".to_owned()));
blob.to_jsval(*cx, message.handle_mut());
},
BinaryType::Arraybuffer => {
diff --git a/components/script/dom/xmlhttprequest.rs b/components/script/dom/xmlhttprequest.rs
index 1953aeddb60..ce90aed8f44 100644
--- a/components/script/dom/xmlhttprequest.rs
+++ b/components/script/dom/xmlhttprequest.rs
@@ -18,7 +18,7 @@ use crate::dom::bindings::refcounted::Trusted;
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject};
use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
use crate::dom::bindings::str::{is_token, ByteString, DOMString, USVString};
-use crate::dom::blob::{Blob, BlobImpl};
+use crate::dom::blob::{normalize_type_string, Blob};
use crate::dom::document::DocumentSource;
use crate::dom::document::{Document, HasBrowsingContext, IsHTMLDocument};
use crate::dom::event::{Event, EventBubbles, EventCancelable};
@@ -65,6 +65,7 @@ use net_traits::CoreResourceMsg::Fetch;
use net_traits::{FetchChannels, FetchMetadata, FilteredMetadata};
use net_traits::{FetchResponseListener, NetworkError, ReferrerPolicy};
use net_traits::{ResourceFetchTiming, ResourceTimingType};
+use script_traits::serializable::BlobImpl;
use script_traits::DocumentActivity;
use servo_atoms::Atom;
use servo_url::ServoUrl;
@@ -1260,12 +1261,12 @@ impl XMLHttpRequest {
let mime = self
.final_mime_type()
.as_ref()
- .map(|m| m.to_string())
+ .map(|m| normalize_type_string(&m.to_string()))
.unwrap_or("".to_owned());
// Step 3, 4
let bytes = self.response.borrow().to_vec();
- let blob = Blob::new(&self.global(), BlobImpl::new_from_bytes(bytes), mime);
+ let blob = Blob::new(&self.global(), BlobImpl::new_from_bytes(bytes, mime));
self.response_blob.set(Some(&blob));
blob
}
diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs
index 64fd85b210b..f5397d8cce5 100644
--- a/components/script/script_thread.rs
+++ b/components/script/script_thread.rs
@@ -1592,7 +1592,7 @@ impl ScriptThread {
window
.upcast::<GlobalScope>()
- .perform_a_message_port_garbage_collection_checkpoint();
+ .perform_a_dom_garbage_collection_checkpoint();
let pending_reflows = window.get_pending_reflow_count();
if pending_reflows > 0 {