constellation_traits/message_port.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525
/* 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 data structures used for message ports and serializing
//! DOM objects to send across them as per
//! <https://html.spec.whatwg.org/multipage/#serializable-objects>.
//! The implementations are here instead of in `script``, because these
//! types can be sent through the Constellation to other ScriptThreads,
//! and Constellation cannot depend directly on `script`.
use std::cell::RefCell;
use std::collections::{HashMap, VecDeque};
use std::path::PathBuf;
use base::id::{BlobId, DomPointId, MessagePortId};
use log::warn;
use malloc_size_of_derive::MallocSizeOf;
use net_traits::filemanager_thread::RelativePos;
use serde::{Deserialize, Serialize};
use servo_url::ImmutableOrigin;
use strum::{EnumIter, IntoEnumIterator};
use uuid::Uuid;
#[derive(Debug, Deserialize, MallocSizeOf, Serialize)]
enum MessagePortState {
/// <https://html.spec.whatwg.org/multipage/#detached>
Detached,
/// <https://html.spec.whatwg.org/multipage/#port-message-queue>
/// The message-queue of this port is enabled,
/// the boolean represents awaiting completion of a transfer.
Enabled(bool),
/// <https://html.spec.whatwg.org/multipage/#port-message-queue>
/// The message-queue of this port is disabled,
/// the boolean represents awaiting completion of a transfer.
Disabled(bool),
}
#[derive(Debug, Deserialize, MallocSizeOf, Serialize)]
/// The data and logic backing the DOM managed MessagePort.
pub struct MessagePortImpl {
/// The current state of the port.
state: MessagePortState,
/// <https://html.spec.whatwg.org/multipage/#entangle>
entangled_port: Option<MessagePortId>,
/// <https://html.spec.whatwg.org/multipage/#port-message-queue>
message_buffer: Option<VecDeque<PortMessageTask>>,
/// The UUID of this port.
message_port_id: MessagePortId,
}
impl MessagePortImpl {
/// Create a new messageport impl.
pub fn new(port_id: MessagePortId) -> MessagePortImpl {
MessagePortImpl {
state: MessagePortState::Disabled(false),
entangled_port: None,
message_buffer: None,
message_port_id: port_id,
}
}
/// Get the Id.
pub fn message_port_id(&self) -> &MessagePortId {
&self.message_port_id
}
/// Maybe get the Id of the entangled port.
pub fn entangled_port_id(&self) -> Option<MessagePortId> {
self.entangled_port
}
/// Entanged this port with another.
pub fn entangle(&mut self, other_id: MessagePortId) {
self.entangled_port = Some(other_id);
}
/// Is this port enabled?
pub fn enabled(&self) -> bool {
matches!(self.state, MessagePortState::Enabled(_))
}
/// Mark this port as having been shipped.
/// <https://html.spec.whatwg.org/multipage/#has-been-shipped>
pub fn set_has_been_shipped(&mut self) {
match self.state {
MessagePortState::Detached => {
panic!("Messageport set_has_been_shipped called in detached state")
},
MessagePortState::Enabled(_) => self.state = MessagePortState::Enabled(true),
MessagePortState::Disabled(_) => self.state = MessagePortState::Disabled(true),
}
}
/// Handle the completion of the transfer,
/// this is data received from the constellation.
pub fn complete_transfer(&mut self, mut tasks: VecDeque<PortMessageTask>) {
match self.state {
MessagePortState::Detached => return,
MessagePortState::Enabled(_) => self.state = MessagePortState::Enabled(false),
MessagePortState::Disabled(_) => self.state = MessagePortState::Disabled(false),
}
// Note: these are the tasks that were buffered while the transfer was ongoing,
// hence they need to execute first.
// The global will call `start` if we are enabled,
// which will add tasks on the event-loop to dispatch incoming messages.
match self.message_buffer {
Some(ref mut incoming_buffer) => {
while let Some(task) = tasks.pop_back() {
incoming_buffer.push_front(task);
}
},
None => self.message_buffer = Some(tasks),
}
}
/// A message was received from our entangled port,
/// returns an optional task to be dispatched.
pub fn handle_incoming(&mut self, task: PortMessageTask) -> Option<PortMessageTask> {
let should_dispatch = match self.state {
MessagePortState::Detached => return None,
MessagePortState::Enabled(in_transfer) => !in_transfer,
MessagePortState::Disabled(_) => false,
};
if should_dispatch {
Some(task)
} else {
match self.message_buffer {
Some(ref mut buffer) => {
buffer.push_back(task);
},
None => {
let mut queue = VecDeque::new();
queue.push_back(task);
self.message_buffer = Some(queue);
},
}
None
}
}
/// <https://html.spec.whatwg.org/multipage/#dom-messageport-start>
/// returns an optional queue of tasks that were buffered while the port was disabled.
pub fn start(&mut self) -> Option<VecDeque<PortMessageTask>> {
match self.state {
MessagePortState::Detached => return None,
MessagePortState::Enabled(_) => {},
MessagePortState::Disabled(in_transfer) => {
self.state = MessagePortState::Enabled(in_transfer);
},
}
if let MessagePortState::Enabled(true) = self.state {
return None;
}
self.message_buffer.take()
}
/// <https://html.spec.whatwg.org/multipage/#dom-messageport-close>
pub fn close(&mut self) {
// Step 1
self.state = MessagePortState::Detached;
}
}
/// A data-holder for serialized data and transferred objects.
/// <https://html.spec.whatwg.org/multipage/#structuredserializewithtransfer>
#[derive(Debug, Default, Deserialize, MallocSizeOf, Serialize)]
pub struct StructuredSerializedData {
/// Data serialized by SpiderMonkey.
pub serialized: Vec<u8>,
/// Serialized in a structured callback,
pub blobs: Option<HashMap<BlobId, BlobImpl>>,
/// Serialized point objects.
pub points: Option<HashMap<DomPointId, DomPoint>>,
/// Transferred objects.
pub ports: Option<HashMap<MessagePortId, MessagePortImpl>>,
}
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<HashMap<Self::Id, Self>>;
/// The field into which to place cloned values.
fn destination(data: &mut StructuredSerializedData) -> &mut Option<HashMap<Self::Id, Self>>;
}
/// All the DOM interfaces that can be serialized.
#[derive(Clone, Copy, Debug, EnumIter)]
pub enum Serializable {
/// The `Blob` interface.
Blob,
/// The `DOMPoint` interface.
DomPoint,
/// The `DOMPointReadOnly` interface.
DomPointReadOnly,
}
impl Serializable {
fn clone_values(&self) -> fn(&StructuredSerializedData, &mut StructuredSerializedData) {
match self {
Serializable::Blob => StructuredSerializedData::clone_all_of_type::<BlobImpl>,
Serializable::DomPointReadOnly => {
StructuredSerializedData::clone_all_of_type::<DomPoint>
},
Serializable::DomPoint => StructuredSerializedData::clone_all_of_type::<DomPoint>,
}
}
}
/// All the DOM interfaces that can be transferred.
#[derive(Clone, Copy, Debug, EnumIter)]
pub enum Transferrable {
/// The `MessagePort` interface.
MessagePort,
}
impl StructuredSerializedData {
fn is_empty(&self, val: Transferrable) -> bool {
fn is_field_empty<K, V>(field: &Option<HashMap<K, V>>) -> bool {
field.as_ref().is_some_and(|h| h.is_empty())
}
match val {
Transferrable::MessagePort => is_field_empty(&self.ports),
}
}
/// Clone all values of the same type stored in this StructuredSerializedData
/// into another instance.
fn clone_all_of_type<T: BroadcastClone>(&self, cloned: &mut StructuredSerializedData) {
let existing = T::source(self);
let Some(existing) = existing else { return };
let mut clones = HashMap::with_capacity(existing.len());
for (original_id, obj) in existing.iter() {
if let Some(clone) = obj.clone_for_broadcast() {
clones.insert(*original_id, clone);
}
}
*T::destination(cloned) = Some(clones);
}
/// Clone the serialized data for use with broadcast-channels.
pub fn clone_for_broadcast(&self) -> StructuredSerializedData {
for transferrable in Transferrable::iter() {
if !self.is_empty(transferrable) {
// Not panicking only because this is called from the constellation.
warn!(
"Attempt to broadcast structured serialized data including {:?} (should never happen).",
transferrable,
);
}
}
let serialized = self.serialized.clone();
let mut cloned = StructuredSerializedData {
serialized,
..Default::default()
};
for serializable in Serializable::iter() {
let clone_impl = serializable.clone_values();
clone_impl(self, &mut cloned);
}
cloned
}
}
/// A task on the <https://html.spec.whatwg.org/multipage/#port-message-queue>
#[derive(Debug, Deserialize, MallocSizeOf, Serialize)]
pub struct PortMessageTask {
/// The origin of this task.
pub origin: ImmutableOrigin,
/// A data-holder for serialized data and transferred objects.
pub data: StructuredSerializedData,
}
/// Messages for communication between the constellation and a global managing ports.
#[derive(Debug, Deserialize, Serialize)]
pub enum MessagePortMsg {
/// Complete the transfer for a batch of ports.
CompleteTransfer(HashMap<MessagePortId, VecDeque<PortMessageTask>>),
/// Complete the transfer of a single port,
/// whose transfer was pending because it had been requested
/// while a previous failed transfer was being rolled-back.
CompletePendingTransfer(MessagePortId, VecDeque<PortMessageTask>),
/// Remove a port, the entangled one doesn't exists anymore.
RemoveMessagePort(MessagePortId),
/// Handle a new port-message-task.
NewTask(MessagePortId, PortMessageTask),
}
/// Message for communication between the constellation and a global managing broadcast channels.
#[derive(Debug, Deserialize, Serialize)]
pub struct BroadcastMsg {
/// 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 BroadcastMsg {
fn clone(&self) -> BroadcastMsg {
BroadcastMsg {
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<std::collections::HashMap<Self::Id, Self>> {
&data.blobs
}
fn destination(
data: &mut StructuredSerializedData,
) -> &mut Option<std::collections::HashMap<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(rel_pos: RelativePos, parent: BlobId, type_string: String) -> BlobImpl {
let blob_id = BlobId::new();
let blob_data = BlobData::Sliced(parent, rel_pos);
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<std::collections::HashMap<Self::Id, Self>> {
&data.points
}
fn destination(
data: &mut StructuredSerializedData,
) -> &mut Option<std::collections::HashMap<Self::Id, Self>> {
&mut data.points
}
fn clone_for_broadcast(&self) -> Option<Self> {
Some(self.clone())
}
}