mirror of
https://github.com/servo/servo
synced 2026-05-09 08:32:31 +02:00
This splits `Snapshot` into two structs: - `Snapshot`: A non-serializable owned image buffer that might contain either shared memory or a `Vec<u8>`. When mutated the shared memory version is converted into a `Vec<u8>` avoiding a copy of the data until absolutely necessary. - `SharedSnapshot`: A serialzable version of `Snapshot` that contains only shared memory and is suitable for sending across IPC channels. This version is cheaply convertible from and to owned `Snapshot`s as long as the source is also a shared memory `Snapshot`. In addition, it does copyless conversions of `RasterImage` into `Snapshot`s (including for frame offsets in animated images). Finally, there are a few minor changes to try harder not to have to do a `transform()` operation. Testing: This should not change observable behavior, so is covered by existing tests. It should come with a mild performance improvement and open up the opportunity for others. Fixes: #36594. Signed-off-by: Martin Robinson <mrobinson@igalia.com>
495 lines
14 KiB
Rust
495 lines
14 KiB
Rust
/* 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 https://mozilla.org/MPL/2.0/. */
|
|
|
|
//! This module contains implementations in script that are serializable,
|
|
//! as per <https://html.spec.whatwg.org/multipage/#serializable-objects>.
|
|
//! The implementations are here instead of in script as they need to
|
|
//! be passed through the Constellation.
|
|
|
|
use std::cell::RefCell;
|
|
use std::path::PathBuf;
|
|
|
|
use base::id::{
|
|
BlobId, DomExceptionId, DomMatrixId, DomPointId, DomQuadId, DomRectId, ImageBitmapId,
|
|
ImageDataId, QuotaExceededErrorId,
|
|
};
|
|
use euclid::default::Transform3D;
|
|
use malloc_size_of_derive::MallocSizeOf;
|
|
use net_traits::filemanager_thread::RelativePos;
|
|
use pixels::SharedSnapshot;
|
|
use rustc_hash::FxHashMap;
|
|
use serde::{Deserialize, Serialize};
|
|
use servo_url::ImmutableOrigin;
|
|
use strum::EnumIter;
|
|
use uuid::Uuid;
|
|
|
|
use super::StructuredSerializedData;
|
|
|
|
pub(crate) trait BroadcastClone
|
|
where
|
|
Self: Sized,
|
|
{
|
|
/// The ID type that uniquely identify each value.
|
|
type Id: Eq + std::hash::Hash + Copy;
|
|
/// Clone this value so that it can be reused with a broadcast channel.
|
|
/// Only return None if cloning is impossible.
|
|
fn clone_for_broadcast(&self) -> Option<Self>;
|
|
/// The field from which to clone values.
|
|
fn source(data: &StructuredSerializedData) -> &Option<FxHashMap<Self::Id, Self>>;
|
|
/// The field into which to place cloned values.
|
|
fn destination(data: &mut StructuredSerializedData) -> &mut Option<FxHashMap<Self::Id, Self>>;
|
|
}
|
|
|
|
/// All the DOM interfaces that can be serialized.
|
|
///
|
|
/// NOTE: Variants which are derived from other serializable interfaces must come before their
|
|
/// parents because serialization is attempted in order of the variants.
|
|
#[derive(Clone, Copy, Debug, EnumIter)]
|
|
pub enum Serializable {
|
|
/// The `Blob` interface.
|
|
Blob,
|
|
/// The `DOMPoint` interface.
|
|
DomPoint,
|
|
/// The `DOMPointReadOnly` interface.
|
|
DomPointReadOnly,
|
|
/// The `DOMRect` interface.
|
|
DomRect,
|
|
/// The `DOMRectReadOnly` interface.
|
|
DomRectReadOnly,
|
|
/// The `DOMQuad` interface.
|
|
DomQuad,
|
|
/// The `DOMMatrix` interface.
|
|
DomMatrix,
|
|
/// The `DOMMatrixReadOnly` interface.
|
|
DomMatrixReadOnly,
|
|
/// The `QuotaExceededError` interface.
|
|
QuotaExceededError,
|
|
/// The `DOMException` interface.
|
|
DomException,
|
|
/// The `ImageBitmap` interface.
|
|
ImageBitmap,
|
|
/// The `ImageData` interface.
|
|
ImageData,
|
|
}
|
|
|
|
impl Serializable {
|
|
pub(super) fn clone_values(
|
|
&self,
|
|
) -> fn(&StructuredSerializedData, &mut StructuredSerializedData) {
|
|
match self {
|
|
Serializable::Blob => StructuredSerializedData::clone_all_of_type::<BlobImpl>,
|
|
Serializable::DomPoint => StructuredSerializedData::clone_all_of_type::<DomPoint>,
|
|
Serializable::DomPointReadOnly => {
|
|
StructuredSerializedData::clone_all_of_type::<DomPoint>
|
|
},
|
|
Serializable::DomRect => StructuredSerializedData::clone_all_of_type::<DomRect>,
|
|
Serializable::DomRectReadOnly => StructuredSerializedData::clone_all_of_type::<DomRect>,
|
|
Serializable::DomQuad => StructuredSerializedData::clone_all_of_type::<DomQuad>,
|
|
Serializable::DomMatrix => StructuredSerializedData::clone_all_of_type::<DomMatrix>,
|
|
Serializable::DomMatrixReadOnly => {
|
|
StructuredSerializedData::clone_all_of_type::<DomMatrix>
|
|
},
|
|
Serializable::DomException => {
|
|
StructuredSerializedData::clone_all_of_type::<DomException>
|
|
},
|
|
Serializable::ImageBitmap => {
|
|
StructuredSerializedData::clone_all_of_type::<SerializableImageBitmap>
|
|
},
|
|
Serializable::QuotaExceededError => {
|
|
StructuredSerializedData::clone_all_of_type::<SerializableQuotaExceededError>
|
|
},
|
|
Serializable::ImageData => {
|
|
StructuredSerializedData::clone_all_of_type::<SerializableImageData>
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Message for communication between the constellation and a global managing broadcast channels.
|
|
#[derive(Debug, Deserialize, Serialize)]
|
|
pub struct BroadcastChannelMsg {
|
|
/// The origin of this message.
|
|
pub origin: ImmutableOrigin,
|
|
/// The name of the channel.
|
|
pub channel_name: String,
|
|
/// A data-holder for serialized data.
|
|
pub data: StructuredSerializedData,
|
|
}
|
|
|
|
impl Clone for BroadcastChannelMsg {
|
|
fn clone(&self) -> BroadcastChannelMsg {
|
|
BroadcastChannelMsg {
|
|
data: self.data.clone_for_broadcast(),
|
|
origin: self.origin.clone(),
|
|
channel_name: self.channel_name.clone(),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// File-based blob
|
|
#[derive(Debug, Deserialize, MallocSizeOf, Serialize)]
|
|
pub struct FileBlob {
|
|
#[ignore_malloc_size_of = "Uuid are hard(not really)"]
|
|
id: Uuid,
|
|
#[ignore_malloc_size_of = "PathBuf are hard"]
|
|
name: Option<PathBuf>,
|
|
cache: RefCell<Option<Vec<u8>>>,
|
|
size: u64,
|
|
}
|
|
|
|
impl FileBlob {
|
|
/// Create a new file blob.
|
|
pub fn new(id: Uuid, name: Option<PathBuf>, cache: Option<Vec<u8>>, size: u64) -> FileBlob {
|
|
FileBlob {
|
|
id,
|
|
name,
|
|
cache: RefCell::new(cache),
|
|
size,
|
|
}
|
|
}
|
|
|
|
/// Get the size of the file.
|
|
pub fn get_size(&self) -> u64 {
|
|
self.size
|
|
}
|
|
|
|
/// Get the cached file data, if any.
|
|
pub fn get_cache(&self) -> Option<Vec<u8>> {
|
|
self.cache.borrow().clone()
|
|
}
|
|
|
|
/// Cache data.
|
|
pub fn cache_bytes(&self, bytes: Vec<u8>) {
|
|
*self.cache.borrow_mut() = Some(bytes);
|
|
}
|
|
|
|
/// Get the file id.
|
|
pub fn get_id(&self) -> Uuid {
|
|
self.id
|
|
}
|
|
}
|
|
|
|
impl BroadcastClone for BlobImpl {
|
|
type Id = BlobId;
|
|
|
|
fn source(data: &StructuredSerializedData) -> &Option<FxHashMap<Self::Id, Self>> {
|
|
&data.blobs
|
|
}
|
|
|
|
fn destination(data: &mut StructuredSerializedData) -> &mut Option<FxHashMap<Self::Id, Self>> {
|
|
&mut data.blobs
|
|
}
|
|
|
|
fn clone_for_broadcast(&self) -> Option<Self> {
|
|
let type_string = self.type_string();
|
|
|
|
if let BlobData::Memory(bytes) = self.blob_data() {
|
|
let blob_clone = BlobImpl::new_from_bytes(bytes.clone(), type_string);
|
|
|
|
// Note: we insert the blob at the original id,
|
|
// otherwise this will not match the storage key as serialized by SM in `serialized`.
|
|
// The clone has it's own new Id however.
|
|
return Some(blob_clone);
|
|
} else {
|
|
// Not panicking only because this is called from the constellation.
|
|
log::warn!("Serialized blob not in memory format(should never happen).");
|
|
}
|
|
None
|
|
}
|
|
}
|
|
|
|
/// The data backing a DOM Blob.
|
|
#[derive(Debug, Deserialize, MallocSizeOf, Serialize)]
|
|
pub struct BlobImpl {
|
|
/// UUID of the blob.
|
|
blob_id: BlobId,
|
|
/// Content-type string
|
|
type_string: String,
|
|
/// Blob data-type.
|
|
blob_data: BlobData,
|
|
/// Sliced blobs referring to this one.
|
|
slices: Vec<BlobId>,
|
|
}
|
|
|
|
/// Different backends of Blob
|
|
#[derive(Debug, Deserialize, MallocSizeOf, Serialize)]
|
|
pub enum BlobData {
|
|
/// 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-id 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(BlobId, RelativePos),
|
|
}
|
|
|
|
impl BlobImpl {
|
|
/// Construct memory-backed BlobImpl
|
|
pub fn new_from_bytes(bytes: Vec<u8>, type_string: String) -> BlobImpl {
|
|
let blob_id = BlobId::new();
|
|
let blob_data = BlobData::Memory(bytes);
|
|
BlobImpl {
|
|
blob_id,
|
|
type_string,
|
|
blob_data,
|
|
slices: vec![],
|
|
}
|
|
}
|
|
|
|
/// Construct file-backed BlobImpl from File ID
|
|
pub fn new_from_file(file_id: Uuid, name: PathBuf, size: u64, type_string: String) -> BlobImpl {
|
|
let blob_id = BlobId::new();
|
|
let blob_data = BlobData::File(FileBlob {
|
|
id: file_id,
|
|
name: Some(name),
|
|
cache: RefCell::new(None),
|
|
size,
|
|
});
|
|
BlobImpl {
|
|
blob_id,
|
|
type_string,
|
|
blob_data,
|
|
slices: vec![],
|
|
}
|
|
}
|
|
|
|
/// Construct a BlobImpl from a slice of a parent.
|
|
pub fn new_sliced(range: RelativePos, parent: BlobId, type_string: String) -> BlobImpl {
|
|
let blob_id = BlobId::new();
|
|
let blob_data = BlobData::Sliced(parent, range);
|
|
BlobImpl {
|
|
blob_id,
|
|
type_string,
|
|
blob_data,
|
|
slices: vec![],
|
|
}
|
|
}
|
|
|
|
/// Get a clone of the blob-id
|
|
pub fn blob_id(&self) -> BlobId {
|
|
self.blob_id
|
|
}
|
|
|
|
/// Get a clone of the type-string
|
|
pub fn type_string(&self) -> String {
|
|
self.type_string.clone()
|
|
}
|
|
|
|
/// Get a mutable ref to the data
|
|
pub fn blob_data(&self) -> &BlobData {
|
|
&self.blob_data
|
|
}
|
|
|
|
/// Get a mutable ref to the data
|
|
pub fn blob_data_mut(&mut self) -> &mut BlobData {
|
|
&mut self.blob_data
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
|
|
/// A serializable version of the DOMPoint/DOMPointReadOnly interface.
|
|
pub struct DomPoint {
|
|
/// The x coordinate.
|
|
pub x: f64,
|
|
/// The y coordinate.
|
|
pub y: f64,
|
|
/// The z coordinate.
|
|
pub z: f64,
|
|
/// The w coordinate.
|
|
pub w: f64,
|
|
}
|
|
|
|
impl BroadcastClone for DomPoint {
|
|
type Id = DomPointId;
|
|
|
|
fn source(data: &StructuredSerializedData) -> &Option<FxHashMap<Self::Id, Self>> {
|
|
&data.points
|
|
}
|
|
|
|
fn destination(data: &mut StructuredSerializedData) -> &mut Option<FxHashMap<Self::Id, Self>> {
|
|
&mut data.points
|
|
}
|
|
|
|
fn clone_for_broadcast(&self) -> Option<Self> {
|
|
Some(self.clone())
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
|
|
/// A serializable version of the DOMRect/DOMRectReadOnly interface.
|
|
pub struct DomRect {
|
|
/// The x coordinate.
|
|
pub x: f64,
|
|
/// The y coordinate.
|
|
pub y: f64,
|
|
/// The width.
|
|
pub width: f64,
|
|
/// The height.
|
|
pub height: f64,
|
|
}
|
|
|
|
impl BroadcastClone for DomRect {
|
|
type Id = DomRectId;
|
|
|
|
fn source(data: &StructuredSerializedData) -> &Option<FxHashMap<Self::Id, Self>> {
|
|
&data.rects
|
|
}
|
|
|
|
fn destination(data: &mut StructuredSerializedData) -> &mut Option<FxHashMap<Self::Id, Self>> {
|
|
&mut data.rects
|
|
}
|
|
|
|
fn clone_for_broadcast(&self) -> Option<Self> {
|
|
Some(self.clone())
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
|
|
/// A serializable version of the DOMQuad interface.
|
|
pub struct DomQuad {
|
|
/// The first point.
|
|
pub p1: DomPoint,
|
|
/// The second point.
|
|
pub p2: DomPoint,
|
|
/// The third point.
|
|
pub p3: DomPoint,
|
|
/// The fourth point.
|
|
pub p4: DomPoint,
|
|
}
|
|
|
|
impl BroadcastClone for DomQuad {
|
|
type Id = DomQuadId;
|
|
|
|
fn source(data: &StructuredSerializedData) -> &Option<FxHashMap<Self::Id, Self>> {
|
|
&data.quads
|
|
}
|
|
|
|
fn destination(data: &mut StructuredSerializedData) -> &mut Option<FxHashMap<Self::Id, Self>> {
|
|
&mut data.quads
|
|
}
|
|
|
|
fn clone_for_broadcast(&self) -> Option<Self> {
|
|
Some(self.clone())
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
|
|
/// A serializable version of the DOMMatrix/DOMMatrixReadOnly interface.
|
|
pub struct DomMatrix {
|
|
/// The matrix.
|
|
pub matrix: Transform3D<f64>,
|
|
/// Whether this matrix represents a 2D transformation.
|
|
pub is_2d: bool,
|
|
}
|
|
|
|
impl BroadcastClone for DomMatrix {
|
|
type Id = DomMatrixId;
|
|
|
|
fn source(data: &StructuredSerializedData) -> &Option<FxHashMap<Self::Id, Self>> {
|
|
&data.matrices
|
|
}
|
|
|
|
fn destination(data: &mut StructuredSerializedData) -> &mut Option<FxHashMap<Self::Id, Self>> {
|
|
&mut data.matrices
|
|
}
|
|
|
|
fn clone_for_broadcast(&self) -> Option<Self> {
|
|
Some(self.clone())
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
|
|
/// A serializable version of the DOMException interface.
|
|
pub struct DomException {
|
|
pub message: String,
|
|
pub name: String,
|
|
}
|
|
|
|
impl BroadcastClone for DomException {
|
|
type Id = DomExceptionId;
|
|
|
|
fn source(data: &StructuredSerializedData) -> &Option<FxHashMap<Self::Id, Self>> {
|
|
&data.exceptions
|
|
}
|
|
|
|
fn destination(data: &mut StructuredSerializedData) -> &mut Option<FxHashMap<Self::Id, Self>> {
|
|
&mut data.exceptions
|
|
}
|
|
|
|
fn clone_for_broadcast(&self) -> Option<Self> {
|
|
Some(self.clone())
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
|
|
/// A serializable version of the QuotaExceededError interface.
|
|
pub struct SerializableQuotaExceededError {
|
|
pub dom_exception: DomException,
|
|
pub quota: Option<f64>,
|
|
pub requested: Option<f64>,
|
|
}
|
|
|
|
impl BroadcastClone for SerializableQuotaExceededError {
|
|
type Id = QuotaExceededErrorId;
|
|
|
|
fn source(data: &StructuredSerializedData) -> &Option<FxHashMap<Self::Id, Self>> {
|
|
&data.quota_exceeded_errors
|
|
}
|
|
|
|
fn destination(data: &mut StructuredSerializedData) -> &mut Option<FxHashMap<Self::Id, Self>> {
|
|
&mut data.quota_exceeded_errors
|
|
}
|
|
|
|
fn clone_for_broadcast(&self) -> Option<Self> {
|
|
Some(self.clone())
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
|
|
/// A serializable version of the ImageBitmap interface.
|
|
pub struct SerializableImageBitmap {
|
|
pub bitmap_data: SharedSnapshot,
|
|
}
|
|
|
|
impl BroadcastClone for SerializableImageBitmap {
|
|
type Id = ImageBitmapId;
|
|
|
|
fn source(data: &StructuredSerializedData) -> &Option<FxHashMap<Self::Id, Self>> {
|
|
&data.image_bitmaps
|
|
}
|
|
|
|
fn destination(data: &mut StructuredSerializedData) -> &mut Option<FxHashMap<Self::Id, Self>> {
|
|
&mut data.image_bitmaps
|
|
}
|
|
|
|
fn clone_for_broadcast(&self) -> Option<Self> {
|
|
Some(self.clone())
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
|
|
pub struct SerializableImageData {
|
|
pub data: Vec<u8>,
|
|
pub width: u32,
|
|
pub height: u32,
|
|
}
|
|
|
|
impl BroadcastClone for SerializableImageData {
|
|
type Id = ImageDataId;
|
|
|
|
fn source(data: &StructuredSerializedData) -> &Option<FxHashMap<Self::Id, Self>> {
|
|
&data.image_data
|
|
}
|
|
|
|
fn destination(data: &mut StructuredSerializedData) -> &mut Option<FxHashMap<Self::Id, Self>> {
|
|
&mut data.image_data
|
|
}
|
|
|
|
fn clone_for_broadcast(&self) -> Option<Self> {
|
|
Some(self.clone())
|
|
}
|
|
}
|