mirror of
https://github.com/servo/servo.git
synced 2025-08-03 04:30:10 +01:00
Auto merge of #23637 - gterzian:continue-message-port, r=jdm
Continue message port <!-- Please describe your changes on the following line: --> Fixes #7457. Fixes #12715. Fixes #12717. Fixes #16095. Fixes #18969. --- <!-- 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/23637) <!-- Reviewable:end -->
This commit is contained in:
commit
a905916ede
113 changed files with 2792 additions and 787 deletions
|
@ -4,15 +4,19 @@
|
|||
|
||||
use crate::dom::bindings::refcounted::Trusted;
|
||||
use crate::dom::bindings::reflector::DomObject;
|
||||
use crate::dom::bindings::structuredclone::StructuredCloneData;
|
||||
use crate::script_runtime::CommonScriptMsg;
|
||||
use script_traits::StructuredSerializedData;
|
||||
use servo_url::ImmutableOrigin;
|
||||
|
||||
/// Messages used to control the worker event loops
|
||||
pub enum WorkerScriptMsg {
|
||||
/// Common variants associated with the script messages
|
||||
Common(CommonScriptMsg),
|
||||
/// Message sent through Worker.postMessage
|
||||
DOMMessage(StructuredCloneData),
|
||||
DOMMessage {
|
||||
origin: ImmutableOrigin,
|
||||
data: StructuredSerializedData,
|
||||
},
|
||||
}
|
||||
|
||||
pub struct SimpleWorkerErrorHandler<T: DomObject> {
|
||||
|
|
|
@ -75,7 +75,7 @@ impl ScriptPort for Receiver<DedicatedWorkerScriptMsg> {
|
|||
};
|
||||
match common_msg {
|
||||
WorkerScriptMsg::Common(script_msg) => Ok(script_msg),
|
||||
WorkerScriptMsg::DOMMessage(_) => panic!("unexpected worker event message!"),
|
||||
WorkerScriptMsg::DOMMessage { .. } => panic!("unexpected worker event message!"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -155,4 +155,7 @@ pub fn run_worker_event_loop<T, TimerMsg, WorkerMsg, Event>(
|
|||
.upcast::<GlobalScope>()
|
||||
.perform_a_microtask_checkpoint();
|
||||
}
|
||||
worker_scope
|
||||
.upcast::<GlobalScope>()
|
||||
.perform_a_message_port_garbage_collection_checkpoint();
|
||||
}
|
||||
|
|
|
@ -30,6 +30,10 @@ DOMInterfaces = {
|
|||
'weakReferenceable': True,
|
||||
},
|
||||
|
||||
'MessagePort': {
|
||||
'weakReferenceable': True,
|
||||
},
|
||||
|
||||
#FIXME(jdm): This should be 'register': False, but then we don't generate enum types
|
||||
'TestBinding': {
|
||||
'inCompartments': ['PromiseAttribute', 'PromiseNativeHandler'],
|
||||
|
|
|
@ -154,6 +154,7 @@ pub mod settings_stack;
|
|||
pub mod str;
|
||||
pub mod structuredclone;
|
||||
pub mod trace;
|
||||
pub mod transferable;
|
||||
pub mod utils;
|
||||
pub mod weakref;
|
||||
pub mod xmlname;
|
||||
|
|
|
@ -6,12 +6,15 @@
|
|||
//! (https://html.spec.whatwg.org/multipage/#safe-passing-of-structured-data).
|
||||
|
||||
use crate::compartments::enter_realm;
|
||||
use crate::dom::bindings::conversions::root_from_handleobject;
|
||||
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::transferable::Transferable;
|
||||
use crate::dom::blob::{Blob, BlobImpl};
|
||||
use crate::dom::globalscope::GlobalScope;
|
||||
use crate::dom::messageport::MessagePort;
|
||||
use crate::script_runtime::JSContext as SafeJSContext;
|
||||
use js::glue::CopyJSStructuredCloneData;
|
||||
use js::glue::DeleteJSAutoStructuredCloneBuffer;
|
||||
use js::glue::GetLengthOfJSStructuredCloneData;
|
||||
|
@ -28,12 +31,15 @@ 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::{Handle, HandleValue, MutableHandleValue};
|
||||
use libc::size_t;
|
||||
use js::rust::{CustomAutoRooterGuard, HandleValue, MutableHandleValue};
|
||||
use msg::constellation_msg::MessagePortId;
|
||||
use script_traits::transferable::MessagePortImpl;
|
||||
use script_traits::StructuredSerializedData;
|
||||
use std::collections::HashMap;
|
||||
use std::os::raw;
|
||||
use std::ptr;
|
||||
use std::slice;
|
||||
|
||||
// TODO: Should we add Min and Max const to https://github.com/servo/rust-mozjs/blob/master/src/consts.rs?
|
||||
// TODO: Determine for sure which value Min and Max should have.
|
||||
|
@ -44,6 +50,7 @@ enum StructuredCloneTags {
|
|||
/// To support additional types, add new tags with values incremented from the last one before Max.
|
||||
Min = 0xFFFF8000,
|
||||
DomBlob = 0xFFFF8001,
|
||||
MessagePort = 0xFFFF8002,
|
||||
Max = 0xFFFFFFFF,
|
||||
}
|
||||
|
||||
|
@ -126,19 +133,24 @@ impl StructuredCloneReader {
|
|||
unsafe fn read_blob(
|
||||
cx: *mut JSContext,
|
||||
r: *mut JSStructuredCloneReader,
|
||||
sc_holder: &mut StructuredCloneHolder,
|
||||
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 blob = Blob::new(
|
||||
let read_blob = Blob::new(
|
||||
&target_global,
|
||||
BlobImpl::new_from_bytes(blob_buffer),
|
||||
type_str,
|
||||
);
|
||||
let js_object = blob.reflector().get_jsobject().get();
|
||||
sc_holder.blob = Some(blob);
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -171,7 +183,7 @@ 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 StructuredCloneHolder));
|
||||
return read_blob(cx, r, &mut *(closure as *mut StructuredDataHolder));
|
||||
}
|
||||
return ptr::null_mut();
|
||||
}
|
||||
|
@ -182,33 +194,55 @@ unsafe extern "C" fn write_callback(
|
|||
obj: RawHandleObject,
|
||||
_closure: *mut raw::c_void,
|
||||
) -> bool {
|
||||
if let Ok(blob) = root_from_handleobject::<Blob>(Handle::from_raw(obj), cx) {
|
||||
if let Ok(blob) = root_from_object::<Blob>(*obj, cx) {
|
||||
return write_blob(blob, w).is_ok();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
unsafe extern "C" fn read_transfer_callback(
|
||||
_cx: *mut JSContext,
|
||||
cx: *mut JSContext,
|
||||
_r: *mut JSStructuredCloneReader,
|
||||
_tag: u32,
|
||||
tag: u32,
|
||||
_content: *mut raw::c_void,
|
||||
_extra_data: u64,
|
||||
_closure: *mut raw::c_void,
|
||||
_return_object: RawMutableHandleObject,
|
||||
extra_data: u64,
|
||||
closure: *mut raw::c_void,
|
||||
return_object: RawMutableHandleObject,
|
||||
) -> bool {
|
||||
if tag == StructuredCloneTags::MessagePort as u32 {
|
||||
let mut sc_holder = &mut *(closure as *mut StructuredDataHolder);
|
||||
let owner = GlobalScope::from_context(cx);
|
||||
if let Ok(_) = <MessagePort as Transferable>::transfer_receive(
|
||||
&owner,
|
||||
&mut sc_holder,
|
||||
extra_data,
|
||||
return_object,
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#structuredserializewithtransfer>
|
||||
unsafe extern "C" fn write_transfer_callback(
|
||||
_cx: *mut JSContext,
|
||||
_obj: RawHandleObject,
|
||||
_closure: *mut raw::c_void,
|
||||
_tag: *mut u32,
|
||||
_ownership: *mut TransferableOwnership,
|
||||
cx: *mut JSContext,
|
||||
obj: RawHandleObject,
|
||||
closure: *mut raw::c_void,
|
||||
tag: *mut u32,
|
||||
ownership: *mut TransferableOwnership,
|
||||
_content: *mut *mut raw::c_void,
|
||||
_extra_data: *mut u64,
|
||||
extra_data: *mut u64,
|
||||
) -> bool {
|
||||
if let Ok(port) = root_from_object::<MessagePort>(*obj, cx) {
|
||||
*tag = StructuredCloneTags::MessagePort as u32;
|
||||
*ownership = TransferableOwnership::SCTAG_TMO_CUSTOM;
|
||||
let mut sc_holder = &mut *(closure as *mut StructuredDataHolder);
|
||||
if let Ok(data) = port.transfer(&mut sc_holder) {
|
||||
*extra_data = data;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
|
@ -222,10 +256,13 @@ unsafe extern "C" fn free_transfer_callback(
|
|||
}
|
||||
|
||||
unsafe extern "C" fn can_transfer_callback(
|
||||
_cx: *mut JSContext,
|
||||
_obj: RawHandleObject,
|
||||
cx: *mut JSContext,
|
||||
obj: RawHandleObject,
|
||||
_closure: *mut raw::c_void,
|
||||
) -> bool {
|
||||
if let Ok(_port) = root_from_object::<MessagePort>(*obj, cx) {
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
|
@ -241,112 +278,143 @@ static STRUCTURED_CLONE_CALLBACKS: JSStructuredCloneCallbacks = JSStructuredClon
|
|||
canTransfer: Some(can_transfer_callback),
|
||||
};
|
||||
|
||||
struct StructuredCloneHolder {
|
||||
blob: Option<DomRoot<Blob>>,
|
||||
/// A data holder for results from, and inputs to, structured-data read/write operations.
|
||||
/// 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 vec of transfer-received DOM ports,
|
||||
/// to be made available to script through a message event.
|
||||
message_ports: Option<Vec<DomRoot<MessagePort>>>,
|
||||
/// A map of port implementations,
|
||||
/// 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 data holder into which transferred ports
|
||||
/// can be written as part of their transfer steps.
|
||||
Write(Option<HashMap<MessagePortId, MessagePortImpl>>),
|
||||
}
|
||||
|
||||
/// A buffer for a structured clone.
|
||||
pub enum StructuredCloneData {
|
||||
/// A non-serializable (default) variant
|
||||
Struct(*mut u64, size_t),
|
||||
/// A variant that can be serialized
|
||||
Vector(Vec<u8>),
|
||||
}
|
||||
|
||||
impl StructuredCloneData {
|
||||
// TODO: should this be unsafe?
|
||||
/// Writes a structured clone. Returns a `DataClone` error if that fails.
|
||||
pub fn write(cx: *mut JSContext, message: HandleValue) -> Fallible<StructuredCloneData> {
|
||||
unsafe {
|
||||
let scbuf = NewJSAutoStructuredCloneBuffer(
|
||||
StructuredCloneScope::DifferentProcess,
|
||||
&STRUCTURED_CLONE_CALLBACKS,
|
||||
);
|
||||
let scdata = &mut ((*scbuf).data_);
|
||||
let policy = CloneDataPolicy {
|
||||
// TODO: SAB?
|
||||
sharedArrayBuffer_: false,
|
||||
};
|
||||
let result = JS_WriteStructuredClone(
|
||||
cx,
|
||||
message,
|
||||
scdata,
|
||||
StructuredCloneScope::DifferentProcess,
|
||||
policy,
|
||||
&STRUCTURED_CLONE_CALLBACKS,
|
||||
ptr::null_mut(),
|
||||
HandleValue::undefined(),
|
||||
);
|
||||
if !result {
|
||||
JS_ClearPendingException(cx);
|
||||
return Err(Error::DataClone);
|
||||
}
|
||||
|
||||
let nbytes = GetLengthOfJSStructuredCloneData(scdata);
|
||||
let mut data = Vec::with_capacity(nbytes);
|
||||
CopyJSStructuredCloneData(scdata, data.as_mut_ptr());
|
||||
data.set_len(nbytes);
|
||||
|
||||
DeleteJSAutoStructuredCloneBuffer(scbuf);
|
||||
|
||||
Ok(StructuredCloneData::Vector(data))
|
||||
/// Writes a structured clone. Returns a `DataClone` error if that fails.
|
||||
pub fn write(
|
||||
cx: SafeJSContext,
|
||||
message: HandleValue,
|
||||
transfer: Option<CustomAutoRooterGuard<Vec<*mut JSObject>>>,
|
||||
) -> Fallible<StructuredSerializedData> {
|
||||
unsafe {
|
||||
rooted!(in(*cx) let mut val = UndefinedValue());
|
||||
if let Some(transfer) = transfer {
|
||||
transfer.to_jsval(*cx, val.handle_mut());
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts a StructuredCloneData to Vec<u8> for inter-thread sharing
|
||||
pub fn move_to_arraybuffer(self) -> Vec<u8> {
|
||||
match self {
|
||||
StructuredCloneData::Struct(data, nbytes) => unsafe {
|
||||
slice::from_raw_parts(data as *mut u8, nbytes).to_vec()
|
||||
},
|
||||
StructuredCloneData::Vector(msg) => msg,
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads a structured clone.
|
||||
///
|
||||
/// Panics if `JS_ReadStructuredClone` fails.
|
||||
fn read_clone(global: &GlobalScope, data: *mut u64, nbytes: size_t, rval: MutableHandleValue) {
|
||||
let cx = global.get_cx();
|
||||
let _ac = enter_realm(&*global);
|
||||
let mut sc_holder = StructuredCloneHolder { blob: None };
|
||||
let mut sc_holder = StructuredDataHolder::Write(None);
|
||||
let sc_holder_ptr = &mut sc_holder as *mut _;
|
||||
unsafe {
|
||||
let scbuf = NewJSAutoStructuredCloneBuffer(
|
||||
StructuredCloneScope::DifferentProcess,
|
||||
&STRUCTURED_CLONE_CALLBACKS,
|
||||
);
|
||||
let scdata = &mut ((*scbuf).data_);
|
||||
|
||||
WriteBytesToJSStructuredCloneData(data as *const u8, nbytes, scdata);
|
||||
|
||||
assert!(JS_ReadStructuredClone(
|
||||
*cx,
|
||||
scdata,
|
||||
JS_STRUCTURED_CLONE_VERSION,
|
||||
StructuredCloneScope::DifferentProcess,
|
||||
rval,
|
||||
&STRUCTURED_CLONE_CALLBACKS,
|
||||
sc_holder_ptr as *mut raw::c_void
|
||||
));
|
||||
|
||||
DeleteJSAutoStructuredCloneBuffer(scbuf);
|
||||
let scbuf = NewJSAutoStructuredCloneBuffer(
|
||||
StructuredCloneScope::DifferentProcess,
|
||||
&STRUCTURED_CLONE_CALLBACKS,
|
||||
);
|
||||
let scdata = &mut ((*scbuf).data_);
|
||||
let policy = CloneDataPolicy {
|
||||
// TODO: SAB?
|
||||
sharedArrayBuffer_: false,
|
||||
};
|
||||
let result = JS_WriteStructuredClone(
|
||||
*cx,
|
||||
message,
|
||||
scdata,
|
||||
StructuredCloneScope::DifferentProcess,
|
||||
policy,
|
||||
&STRUCTURED_CLONE_CALLBACKS,
|
||||
sc_holder_ptr as *mut raw::c_void,
|
||||
val.handle(),
|
||||
);
|
||||
if !result {
|
||||
JS_ClearPendingException(*cx);
|
||||
return Err(Error::DataClone);
|
||||
}
|
||||
}
|
||||
|
||||
/// Thunk for the actual `read_clone` method. Resolves proper variant for read_clone.
|
||||
pub fn read(self, global: &GlobalScope, rval: MutableHandleValue) {
|
||||
match self {
|
||||
StructuredCloneData::Vector(mut vec_msg) => {
|
||||
let nbytes = vec_msg.len();
|
||||
let data = vec_msg.as_mut_ptr() as *mut u64;
|
||||
StructuredCloneData::read_clone(global, data, nbytes, rval);
|
||||
},
|
||||
StructuredCloneData::Struct(data, nbytes) => {
|
||||
StructuredCloneData::read_clone(global, data, nbytes, rval)
|
||||
},
|
||||
}
|
||||
let nbytes = GetLengthOfJSStructuredCloneData(scdata);
|
||||
let mut data = Vec::with_capacity(nbytes);
|
||||
CopyJSStructuredCloneData(scdata, data.as_mut_ptr());
|
||||
data.set_len(nbytes);
|
||||
|
||||
DeleteJSAutoStructuredCloneBuffer(scbuf);
|
||||
|
||||
let mut port_impls = match sc_holder {
|
||||
StructuredDataHolder::Write(port_impls) => port_impls,
|
||||
_ => panic!("Unexpected variant of StructuredDataHolder"),
|
||||
};
|
||||
|
||||
let data = StructuredSerializedData {
|
||||
serialized: data,
|
||||
ports: port_impls.take(),
|
||||
};
|
||||
|
||||
Ok(data)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for StructuredCloneData {}
|
||||
/// Read structured serialized data, possibly containing transferred objects.
|
||||
/// Returns a vec of rooted transfer-received ports, or an error.
|
||||
pub fn read(
|
||||
global: &GlobalScope,
|
||||
mut data: StructuredSerializedData,
|
||||
rval: MutableHandleValue,
|
||||
) -> Result<Vec<DomRoot<MessagePort>>, ()> {
|
||||
let cx = global.get_cx();
|
||||
let _ac = enter_realm(&*global);
|
||||
let mut sc_holder = StructuredDataHolder::Read {
|
||||
blob: None,
|
||||
message_ports: None,
|
||||
port_impls: data.ports.take(),
|
||||
};
|
||||
let sc_holder_ptr = &mut sc_holder as *mut _;
|
||||
unsafe {
|
||||
let scbuf = NewJSAutoStructuredCloneBuffer(
|
||||
StructuredCloneScope::DifferentProcess,
|
||||
&STRUCTURED_CLONE_CALLBACKS,
|
||||
);
|
||||
let scdata = &mut ((*scbuf).data_);
|
||||
|
||||
WriteBytesToJSStructuredCloneData(
|
||||
data.serialized.as_mut_ptr() as *const u8,
|
||||
data.serialized.len(),
|
||||
scdata,
|
||||
);
|
||||
|
||||
let result = JS_ReadStructuredClone(
|
||||
*cx,
|
||||
scdata,
|
||||
JS_STRUCTURED_CLONE_VERSION,
|
||||
StructuredCloneScope::DifferentProcess,
|
||||
rval,
|
||||
&STRUCTURED_CLONE_CALLBACKS,
|
||||
sc_holder_ptr as *mut raw::c_void,
|
||||
);
|
||||
|
||||
DeleteJSAutoStructuredCloneBuffer(scbuf);
|
||||
|
||||
if result {
|
||||
let (mut message_ports, port_impls) = match sc_holder {
|
||||
StructuredDataHolder::Read {
|
||||
message_ports,
|
||||
port_impls,
|
||||
..
|
||||
} => (message_ports, port_impls),
|
||||
_ => panic!("Unexpected variant of StructuredDataHolder"),
|
||||
};
|
||||
|
||||
// Any transfer-received port-impls should have been taken out.
|
||||
assert!(port_impls.is_none());
|
||||
|
||||
match message_ports.take() {
|
||||
Some(ports) => return Ok(ports),
|
||||
None => return Ok(Vec::with_capacity(0)),
|
||||
}
|
||||
}
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -79,7 +79,8 @@ use media::WindowGLContext;
|
|||
use metrics::{InteractiveMetrics, InteractiveWindow};
|
||||
use mime::Mime;
|
||||
use msg::constellation_msg::{
|
||||
BrowsingContextId, HistoryStateId, PipelineId, TopLevelBrowsingContextId,
|
||||
BrowsingContextId, HistoryStateId, MessagePortId, MessagePortRouterId, PipelineId,
|
||||
TopLevelBrowsingContextId,
|
||||
};
|
||||
use net_traits::filemanager_thread::RelativePos;
|
||||
use net_traits::image::base::{Image, ImageMetadata};
|
||||
|
@ -93,6 +94,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::transferable::MessagePortImpl;
|
||||
use script_traits::DrawAPaintImageResult;
|
||||
use script_traits::{DocumentActivity, ScriptToConstellationChan, TimerEventId, TimerSource};
|
||||
use script_traits::{UntrustedNodeAddress, WindowSizeData, WindowSizeType};
|
||||
|
@ -154,6 +156,11 @@ pub unsafe trait JSTraceable {
|
|||
|
||||
unsafe_no_jsmanaged_fields!(Box<dyn TaskBox>, Box<dyn EventLoopWaker>);
|
||||
|
||||
unsafe_no_jsmanaged_fields!(MessagePortImpl);
|
||||
unsafe_no_jsmanaged_fields!(MessagePortId);
|
||||
unsafe_no_jsmanaged_fields!(RefCell<Option<MessagePortId>>);
|
||||
unsafe_no_jsmanaged_fields!(MessagePortRouterId);
|
||||
|
||||
unsafe_no_jsmanaged_fields!(CSSError);
|
||||
|
||||
unsafe_no_jsmanaged_fields!(&'static Encoding);
|
||||
|
|
22
components/script/dom/bindings/transferable.rs
Normal file
22
components/script/dom/bindings/transferable.rs
Normal file
|
@ -0,0 +1,22 @@
|
|||
/* 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 [transferable objects]
|
||||
//! (https://html.spec.whatwg.org/multipage/#transferable-objects).
|
||||
|
||||
use crate::dom::bindings::reflector::DomObject;
|
||||
use crate::dom::bindings::root::DomRoot;
|
||||
use crate::dom::bindings::structuredclone::StructuredDataHolder;
|
||||
use crate::dom::globalscope::GlobalScope;
|
||||
use js::jsapi::MutableHandleObject;
|
||||
|
||||
pub trait Transferable: DomObject {
|
||||
fn transfer(&self, sc_holder: &mut StructuredDataHolder) -> Result<u64, ()>;
|
||||
fn transfer_receive(
|
||||
owner: &DomRoot<GlobalScope>,
|
||||
sc_holder: &mut StructuredDataHolder,
|
||||
extra_data: u64,
|
||||
return_object: MutableHandleObject,
|
||||
) -> Result<(), ()>;
|
||||
}
|
|
@ -10,10 +10,13 @@ use crate::dom::bindings::codegen::PrototypeList::{MAX_PROTO_CHAIN_LENGTH, PROTO
|
|||
use crate::dom::bindings::conversions::{jsstring_to_str, private_from_proto_check};
|
||||
use crate::dom::bindings::error::throw_invalid_this;
|
||||
use crate::dom::bindings::inheritance::TopTypeId;
|
||||
use crate::dom::bindings::root::DomRoot;
|
||||
use crate::dom::bindings::str::DOMString;
|
||||
use crate::dom::bindings::trace::trace_object;
|
||||
use crate::dom::messageport::MessagePort;
|
||||
use crate::dom::windowproxy;
|
||||
use crate::script_runtime::JSContext as SafeJSContext;
|
||||
use js::conversions::ToJSValConvertible;
|
||||
use js::glue::{CallJitGetterOp, CallJitMethodOp, CallJitSetterOp, IsWrapper};
|
||||
use js::glue::{GetCrossCompartmentWrapper, JS_GetReservedSlot, WrapperNew};
|
||||
use js::glue::{UnwrapObjectDynamic, RUST_JSID_TO_INT, RUST_JSID_TO_STRING};
|
||||
|
@ -22,7 +25,7 @@ use js::jsapi::HandleId as RawHandleId;
|
|||
use js::jsapi::HandleObject as RawHandleObject;
|
||||
use js::jsapi::MutableHandleObject as RawMutableHandleObject;
|
||||
use js::jsapi::{AutoIdVector, CallArgs, DOMCallbacks, GetNonCCWObjectGlobal};
|
||||
use js::jsapi::{Heap, JSAutoRealm, JSContext};
|
||||
use js::jsapi::{Heap, JSAutoRealm, JSContext, JS_FreezeObject};
|
||||
use js::jsapi::{JSJitInfo, JSObject, JSTracer, JSWrapObjectCallbacks};
|
||||
use js::jsapi::{JS_EnumerateStandardClasses, JS_GetLatin1StringCharsAndLength};
|
||||
use js::jsapi::{JS_IsExceptionPending, JS_IsGlobalObject};
|
||||
|
@ -117,6 +120,19 @@ impl Clone for DOMJSClass {
|
|||
}
|
||||
unsafe impl Sync for DOMJSClass {}
|
||||
|
||||
/// Returns a JSVal representing a frozen array of ports
|
||||
pub fn message_ports_to_frozen_array(
|
||||
message_ports: &[DomRoot<MessagePort>],
|
||||
cx: SafeJSContext,
|
||||
) -> JSVal {
|
||||
rooted!(in(*cx) let mut ports = UndefinedValue());
|
||||
unsafe { message_ports.to_jsval(*cx, ports.handle_mut()) };
|
||||
|
||||
rooted!(in(*cx) let obj = ports.to_object());
|
||||
unsafe { JS_FreezeObject(*cx, RawHandleObject::from(obj.handle())) };
|
||||
*ports
|
||||
}
|
||||
|
||||
/// Returns the ProtoOrIfaceArray for the given global object.
|
||||
/// Fails if `global` is not a DOM global object.
|
||||
pub fn get_proto_or_iface_array(global: *mut JSObject) -> *mut ProtoOrIfaceArray {
|
||||
|
|
|
@ -10,13 +10,15 @@ use crate::dom::abstractworkerglobalscope::{SendableWorkerScriptChan, WorkerThre
|
|||
use crate::dom::bindings::cell::DomRefCell;
|
||||
use crate::dom::bindings::codegen::Bindings::DedicatedWorkerGlobalScopeBinding;
|
||||
use crate::dom::bindings::codegen::Bindings::DedicatedWorkerGlobalScopeBinding::DedicatedWorkerGlobalScopeMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::MessagePortBinding::PostMessageOptions;
|
||||
use crate::dom::bindings::codegen::Bindings::WorkerBinding::WorkerType;
|
||||
use crate::dom::bindings::error::{ErrorInfo, ErrorResult};
|
||||
use crate::dom::bindings::inheritance::Castable;
|
||||
use crate::dom::bindings::reflector::DomObject;
|
||||
use crate::dom::bindings::root::{DomRoot, RootCollection, ThreadLocalStackRoots};
|
||||
use crate::dom::bindings::str::DOMString;
|
||||
use crate::dom::bindings::structuredclone::StructuredCloneData;
|
||||
use crate::dom::bindings::structuredclone;
|
||||
use crate::dom::bindings::trace::RootedTraceableBox;
|
||||
use crate::dom::errorevent::ErrorEvent;
|
||||
use crate::dom::event::{Event, EventBubbles, EventCancelable, EventStatus};
|
||||
use crate::dom::eventtarget::EventTarget;
|
||||
|
@ -36,10 +38,10 @@ use devtools_traits::DevtoolScriptControlMsg;
|
|||
use dom_struct::dom_struct;
|
||||
use ipc_channel::ipc::{self, IpcReceiver, IpcSender};
|
||||
use ipc_channel::router::ROUTER;
|
||||
use js::jsapi::JSContext;
|
||||
use js::jsapi::JS_AddInterruptCallback;
|
||||
use js::jsapi::{Heap, JSContext, JSObject};
|
||||
use js::jsval::UndefinedValue;
|
||||
use js::rust::HandleValue;
|
||||
use js::rust::{CustomAutoRooter, CustomAutoRooterGuard, HandleValue};
|
||||
use msg::constellation_msg::{PipelineId, TopLevelBrowsingContextId};
|
||||
use net_traits::image_cache::ImageCache;
|
||||
use net_traits::request::{CredentialsMode, Destination, ParserMetadata};
|
||||
|
@ -462,13 +464,24 @@ impl DedicatedWorkerGlobalScope {
|
|||
|
||||
fn handle_script_event(&self, msg: WorkerScriptMsg) {
|
||||
match msg {
|
||||
WorkerScriptMsg::DOMMessage(data) => {
|
||||
WorkerScriptMsg::DOMMessage { origin, data } => {
|
||||
let scope = self.upcast::<WorkerGlobalScope>();
|
||||
let target = self.upcast();
|
||||
let _ac = enter_realm(self);
|
||||
rooted!(in(*scope.get_cx()) let mut message = UndefinedValue());
|
||||
data.read(scope.upcast(), message.handle_mut());
|
||||
MessageEvent::dispatch_jsval(target, scope.upcast(), message.handle(), None, None);
|
||||
if let Ok(ports) = structuredclone::read(scope.upcast(), data, message.handle_mut())
|
||||
{
|
||||
MessageEvent::dispatch_jsval(
|
||||
target,
|
||||
scope.upcast(),
|
||||
message.handle(),
|
||||
Some(&origin.ascii_serialization()),
|
||||
None,
|
||||
ports,
|
||||
);
|
||||
} else {
|
||||
MessageEvent::dispatch_error(target, scope.upcast());
|
||||
}
|
||||
},
|
||||
WorkerScriptMsg::Common(msg) => {
|
||||
self.upcast::<WorkerGlobalScope>().process_event(msg);
|
||||
|
@ -547,6 +560,32 @@ impl DedicatedWorkerGlobalScope {
|
|||
))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/#dom-dedicatedworkerglobalscope-postmessage
|
||||
fn post_message_impl(
|
||||
&self,
|
||||
cx: SafeJSContext,
|
||||
message: HandleValue,
|
||||
transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
|
||||
) -> ErrorResult {
|
||||
let data = structuredclone::write(cx, message, Some(transfer))?;
|
||||
let worker = self.worker.borrow().as_ref().unwrap().clone();
|
||||
let global_scope = self.upcast::<GlobalScope>();
|
||||
let pipeline_id = global_scope.pipeline_id();
|
||||
let origin = global_scope.origin().immutable().ascii_serialization();
|
||||
let task = Box::new(task!(post_worker_message: move || {
|
||||
Worker::handle_message(worker, origin, data);
|
||||
}));
|
||||
self.parent_sender
|
||||
.send(CommonScriptMsg::Task(
|
||||
WorkerEvent,
|
||||
task,
|
||||
Some(pipeline_id),
|
||||
TaskSourceName::DOMManipulation,
|
||||
))
|
||||
.unwrap();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
|
@ -560,24 +599,34 @@ unsafe extern "C" fn interrupt_callback(cx: *mut JSContext) -> bool {
|
|||
}
|
||||
|
||||
impl DedicatedWorkerGlobalScopeMethods for DedicatedWorkerGlobalScope {
|
||||
// https://html.spec.whatwg.org/multipage/#dom-dedicatedworkerglobalscope-postmessage
|
||||
fn PostMessage(&self, cx: SafeJSContext, message: HandleValue) -> ErrorResult {
|
||||
let data = StructuredCloneData::write(*cx, message)?;
|
||||
let worker = self.worker.borrow().as_ref().unwrap().clone();
|
||||
let pipeline_id = self.upcast::<GlobalScope>().pipeline_id();
|
||||
let task = Box::new(task!(post_worker_message: move || {
|
||||
Worker::handle_message(worker, data);
|
||||
}));
|
||||
// TODO: Change this task source to a new `unshipped-port-message-queue` task source
|
||||
self.parent_sender
|
||||
.send(CommonScriptMsg::Task(
|
||||
WorkerEvent,
|
||||
task,
|
||||
Some(pipeline_id),
|
||||
TaskSourceName::DOMManipulation,
|
||||
))
|
||||
.unwrap();
|
||||
Ok(())
|
||||
/// https://html.spec.whatwg.org/multipage/#dom-dedicatedworkerglobalscope-postmessage
|
||||
fn PostMessage(
|
||||
&self,
|
||||
cx: SafeJSContext,
|
||||
message: HandleValue,
|
||||
transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
|
||||
) -> ErrorResult {
|
||||
self.post_message_impl(cx, message, transfer)
|
||||
}
|
||||
|
||||
/// https://html.spec.whatwg.org/multipage/#dom-dedicatedworkerglobalscope-postmessage
|
||||
fn PostMessage_(
|
||||
&self,
|
||||
cx: SafeJSContext,
|
||||
message: HandleValue,
|
||||
options: RootedTraceableBox<PostMessageOptions>,
|
||||
) -> ErrorResult {
|
||||
let mut rooted = CustomAutoRooter::new(
|
||||
options
|
||||
.transfer
|
||||
.as_ref()
|
||||
.unwrap_or(&Vec::with_capacity(0))
|
||||
.iter()
|
||||
.map(|js: &RootedTraceableBox<Heap<*mut JSObject>>| js.get())
|
||||
.collect(),
|
||||
);
|
||||
let guard = CustomAutoRooterGuard::new(*cx, &mut rooted);
|
||||
self.post_message_impl(cx, message, guard)
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/#dom-dedicatedworkerglobalscope-close
|
||||
|
|
|
@ -4,21 +4,23 @@
|
|||
|
||||
use crate::dom::bindings::codegen::Bindings::DissimilarOriginWindowBinding;
|
||||
use crate::dom::bindings::codegen::Bindings::DissimilarOriginWindowBinding::DissimilarOriginWindowMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowPostMessageOptions;
|
||||
use crate::dom::bindings::error::{Error, ErrorResult};
|
||||
use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
|
||||
use crate::dom::bindings::str::DOMString;
|
||||
use crate::dom::bindings::structuredclone::StructuredCloneData;
|
||||
use crate::dom::bindings::str::USVString;
|
||||
use crate::dom::bindings::structuredclone;
|
||||
use crate::dom::bindings::trace::RootedTraceableBox;
|
||||
use crate::dom::dissimilaroriginlocation::DissimilarOriginLocation;
|
||||
use crate::dom::globalscope::GlobalScope;
|
||||
use crate::dom::windowproxy::WindowProxy;
|
||||
use crate::script_runtime::JSContext;
|
||||
use dom_struct::dom_struct;
|
||||
use ipc_channel::ipc;
|
||||
use js::jsapi::{Heap, JSObject};
|
||||
use js::jsval::{JSVal, UndefinedValue};
|
||||
use js::rust::HandleValue;
|
||||
use js::rust::{CustomAutoRooter, CustomAutoRooterGuard, HandleValue};
|
||||
use msg::constellation_msg::PipelineId;
|
||||
use script_traits::ScriptMsg;
|
||||
use servo_url::ImmutableOrigin;
|
||||
use script_traits::{ScriptMsg, StructuredSerializedData};
|
||||
use servo_url::ServoUrl;
|
||||
|
||||
/// Represents a dissimilar-origin `Window` that exists in another script thread.
|
||||
|
@ -133,28 +135,42 @@ impl DissimilarOriginWindowMethods for DissimilarOriginWindow {
|
|||
false
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/#dom-window-postmessage
|
||||
fn PostMessage(&self, cx: JSContext, message: HandleValue, origin: DOMString) -> ErrorResult {
|
||||
// Step 3-5.
|
||||
let origin = match &origin[..] {
|
||||
"*" => None,
|
||||
"/" => {
|
||||
// TODO: Should be the origin of the incumbent settings object.
|
||||
None
|
||||
},
|
||||
url => match ServoUrl::parse(&url) {
|
||||
Ok(url) => Some(url.origin()),
|
||||
Err(_) => return Err(Error::Syntax),
|
||||
},
|
||||
};
|
||||
/// https://html.spec.whatwg.org/multipage/#dom-window-postmessage
|
||||
fn PostMessage(
|
||||
&self,
|
||||
cx: JSContext,
|
||||
message: HandleValue,
|
||||
target_origin: USVString,
|
||||
mut transfer: CustomAutoRooterGuard<Option<Vec<*mut JSObject>>>,
|
||||
) -> ErrorResult {
|
||||
if transfer.is_some() {
|
||||
let mut rooted = CustomAutoRooter::new(transfer.take().unwrap());
|
||||
let transfer = Some(CustomAutoRooterGuard::new(*cx, &mut rooted));
|
||||
self.post_message_impl(&target_origin, cx, message, transfer)
|
||||
} else {
|
||||
self.post_message_impl(&target_origin, cx, message, None)
|
||||
}
|
||||
}
|
||||
|
||||
// Step 1-2, 6-8.
|
||||
// TODO(#12717): Should implement the `transfer` argument.
|
||||
let data = StructuredCloneData::write(*cx, message)?;
|
||||
/// https://html.spec.whatwg.org/multipage/#dom-window-postmessage-options
|
||||
fn PostMessage_(
|
||||
&self,
|
||||
cx: JSContext,
|
||||
message: HandleValue,
|
||||
options: RootedTraceableBox<WindowPostMessageOptions>,
|
||||
) -> ErrorResult {
|
||||
let mut rooted = CustomAutoRooter::new(
|
||||
options
|
||||
.transfer
|
||||
.as_ref()
|
||||
.unwrap_or(&Vec::with_capacity(0))
|
||||
.iter()
|
||||
.map(|js: &RootedTraceableBox<Heap<*mut JSObject>>| js.get())
|
||||
.collect(),
|
||||
);
|
||||
let transfer = Some(CustomAutoRooterGuard::new(*cx, &mut rooted));
|
||||
|
||||
// Step 9.
|
||||
self.post_message(origin, data);
|
||||
Ok(())
|
||||
self.post_message_impl(&options.targetOrigin, cx, message, transfer)
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/#dom-opener
|
||||
|
@ -186,17 +202,54 @@ impl DissimilarOriginWindowMethods for DissimilarOriginWindow {
|
|||
}
|
||||
|
||||
impl DissimilarOriginWindow {
|
||||
pub fn post_message(&self, origin: Option<ImmutableOrigin>, data: StructuredCloneData) {
|
||||
/// https://html.spec.whatwg.org/multipage/#window-post-message-steps
|
||||
fn post_message_impl(
|
||||
&self,
|
||||
target_origin: &USVString,
|
||||
cx: JSContext,
|
||||
message: HandleValue,
|
||||
transfer: Option<CustomAutoRooterGuard<Vec<*mut JSObject>>>,
|
||||
) -> ErrorResult {
|
||||
// Step 6-7.
|
||||
let data = structuredclone::write(cx, message, transfer)?;
|
||||
|
||||
self.post_message(target_origin, data)
|
||||
}
|
||||
|
||||
/// https://html.spec.whatwg.org/multipage/#window-post-message-steps
|
||||
pub fn post_message(
|
||||
&self,
|
||||
target_origin: &USVString,
|
||||
data: StructuredSerializedData,
|
||||
) -> ErrorResult {
|
||||
// Step 1.
|
||||
let target = self.window_proxy.browsing_context_id();
|
||||
// Step 2.
|
||||
let incumbent = match GlobalScope::incumbent() {
|
||||
None => return warn!("postMessage called with no incumbent global"),
|
||||
None => panic!("postMessage called with no incumbent global"),
|
||||
Some(incumbent) => incumbent,
|
||||
};
|
||||
let msg = ScriptMsg::PostMessage {
|
||||
target: self.window_proxy.browsing_context_id(),
|
||||
source: incumbent.pipeline_id(),
|
||||
target_origin: origin,
|
||||
data: data.move_to_arraybuffer(),
|
||||
|
||||
let source_origin = incumbent.origin().immutable().clone();
|
||||
|
||||
// Step 3-5.
|
||||
let target_origin = match target_origin.0[..].as_ref() {
|
||||
"*" => None,
|
||||
"/" => Some(source_origin.clone()),
|
||||
url => match ServoUrl::parse(&url) {
|
||||
Ok(url) => Some(url.origin().clone()),
|
||||
Err(_) => return Err(Error::Syntax),
|
||||
},
|
||||
};
|
||||
let msg = ScriptMsg::PostMessage {
|
||||
target,
|
||||
source: incumbent.pipeline_id(),
|
||||
source_origin,
|
||||
target_origin,
|
||||
data: data,
|
||||
};
|
||||
// Step 8
|
||||
let _ = incumbent.script_to_constellation_chan().send(msg);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1917,10 +1917,11 @@ impl Document {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
let global_scope = self.window.upcast::<GlobalScope>();
|
||||
// Step 10, 14
|
||||
// https://html.spec.whatwg.org/multipage/#unloading-document-cleanup-steps
|
||||
if !self.salvageable.get() {
|
||||
// https://html.spec.whatwg.org/multipage/#unloading-document-cleanup-steps
|
||||
let global_scope = self.window.upcast::<GlobalScope>();
|
||||
// Step 1 of clean-up steps.
|
||||
global_scope.close_event_sources();
|
||||
let msg = ScriptMsg::DiscardDocument;
|
||||
|
|
|
@ -236,6 +236,7 @@ impl EventSourceContext {
|
|||
DOMString::from(self.origin.clone()),
|
||||
None,
|
||||
event_source.last_event_id.borrow().clone(),
|
||||
Vec::with_capacity(0),
|
||||
)
|
||||
};
|
||||
// Step 7
|
||||
|
|
|
@ -10,10 +10,12 @@ use crate::dom::bindings::reflector::reflect_dom_object;
|
|||
use crate::dom::bindings::root::DomRoot;
|
||||
use crate::dom::bindings::str::DOMString;
|
||||
use crate::dom::bindings::trace::RootedTraceableBox;
|
||||
use crate::dom::bindings::utils::message_ports_to_frozen_array;
|
||||
use crate::dom::event::Event;
|
||||
use crate::dom::eventtarget::EventTarget;
|
||||
use crate::dom::extendableevent::ExtendableEvent;
|
||||
use crate::dom::globalscope::GlobalScope;
|
||||
use crate::dom::messageport::MessagePort;
|
||||
use crate::dom::serviceworkerglobalscope::ServiceWorkerGlobalScope;
|
||||
use crate::script_runtime::JSContext;
|
||||
use dom_struct::dom_struct;
|
||||
|
@ -29,6 +31,7 @@ pub struct ExtendableMessageEvent {
|
|||
data: Heap<JSVal>,
|
||||
origin: DOMString,
|
||||
lastEventId: DOMString,
|
||||
ports: Vec<DomRoot<MessagePort>>,
|
||||
}
|
||||
|
||||
impl ExtendableMessageEvent {
|
||||
|
@ -40,12 +43,14 @@ impl ExtendableMessageEvent {
|
|||
data: HandleValue,
|
||||
origin: DOMString,
|
||||
lastEventId: DOMString,
|
||||
ports: Vec<DomRoot<MessagePort>>,
|
||||
) -> DomRoot<ExtendableMessageEvent> {
|
||||
let ev = Box::new(ExtendableMessageEvent {
|
||||
event: ExtendableEvent::new_inherited(),
|
||||
data: Heap::default(),
|
||||
origin: origin,
|
||||
lastEventId: lastEventId,
|
||||
origin,
|
||||
lastEventId,
|
||||
ports,
|
||||
});
|
||||
let ev = reflect_dom_object(ev, global, ExtendableMessageEventBinding::Wrap);
|
||||
{
|
||||
|
@ -71,13 +76,19 @@ impl ExtendableMessageEvent {
|
|||
init.data.handle(),
|
||||
init.origin.clone().unwrap(),
|
||||
init.lastEventId.clone().unwrap(),
|
||||
vec![],
|
||||
);
|
||||
Ok(ev)
|
||||
}
|
||||
}
|
||||
|
||||
impl ExtendableMessageEvent {
|
||||
pub fn dispatch_jsval(target: &EventTarget, scope: &GlobalScope, message: HandleValue) {
|
||||
pub fn dispatch_jsval(
|
||||
target: &EventTarget,
|
||||
scope: &GlobalScope,
|
||||
message: HandleValue,
|
||||
ports: Vec<DomRoot<MessagePort>>,
|
||||
) {
|
||||
let Extendablemessageevent = ExtendableMessageEvent::new(
|
||||
scope,
|
||||
atom!("message"),
|
||||
|
@ -86,6 +97,7 @@ impl ExtendableMessageEvent {
|
|||
message,
|
||||
DOMString::new(),
|
||||
DOMString::new(),
|
||||
ports,
|
||||
);
|
||||
Extendablemessageevent.upcast::<Event>().fire(target);
|
||||
}
|
||||
|
@ -111,4 +123,9 @@ impl ExtendableMessageEventMethods for ExtendableMessageEvent {
|
|||
fn IsTrusted(&self) -> bool {
|
||||
self.event.IsTrusted()
|
||||
}
|
||||
|
||||
/// https://w3c.github.io/ServiceWorker/#extendablemessage-event-ports
|
||||
fn Ports(&self, cx: JSContext) -> JSVal {
|
||||
message_ports_to_frozen_array(self.ports.as_slice(), cx)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,17 +9,21 @@ use crate::dom::bindings::codegen::Bindings::WorkerGlobalScopeBinding::WorkerGlo
|
|||
use crate::dom::bindings::conversions::{root_from_object, root_from_object_static};
|
||||
use crate::dom::bindings::error::{report_pending_exception, ErrorInfo};
|
||||
use crate::dom::bindings::inheritance::Castable;
|
||||
use crate::dom::bindings::refcounted::Trusted;
|
||||
use crate::dom::bindings::reflector::DomObject;
|
||||
use crate::dom::bindings::root::{DomRoot, MutNullableDom};
|
||||
use crate::dom::bindings::settings_stack::{entry_global, incumbent_global, AutoEntryScript};
|
||||
use crate::dom::bindings::str::DOMString;
|
||||
use crate::dom::bindings::weakref::DOMTracker;
|
||||
use crate::dom::bindings::structuredclone;
|
||||
use crate::dom::bindings::weakref::{DOMTracker, WeakRef};
|
||||
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::messageevent::MessageEvent;
|
||||
use crate::dom::messageport::MessagePort;
|
||||
use crate::dom::paintworkletglobalscope::PaintWorkletGlobalScope;
|
||||
use crate::dom::performance::Performance;
|
||||
use crate::dom::window::Window;
|
||||
|
@ -33,36 +37,43 @@ use crate::task_source::dom_manipulation::DOMManipulationTaskSource;
|
|||
use crate::task_source::file_reading::FileReadingTaskSource;
|
||||
use crate::task_source::networking::NetworkingTaskSource;
|
||||
use crate::task_source::performance_timeline::PerformanceTimelineTaskSource;
|
||||
use crate::task_source::port_message::PortMessageQueue;
|
||||
use crate::task_source::remote_event::RemoteEventTaskSource;
|
||||
use crate::task_source::websocket::WebsocketTaskSource;
|
||||
use crate::task_source::TaskSource;
|
||||
use crate::task_source::TaskSourceName;
|
||||
use crate::timers::{IsInterval, OneshotTimerCallback, OneshotTimerHandle};
|
||||
use crate::timers::{OneshotTimers, TimerCallback};
|
||||
use content_security_policy::CspList;
|
||||
use devtools_traits::{ScriptToDevtoolsControlMsg, WorkerId};
|
||||
use dom_struct::dom_struct;
|
||||
use ipc_channel::ipc::IpcSender;
|
||||
use ipc_channel::ipc::{self, IpcSender};
|
||||
use ipc_channel::router::ROUTER;
|
||||
use js::glue::{IsWrapper, UnwrapObjectDynamic};
|
||||
use js::jsapi::JSObject;
|
||||
use js::jsapi::{CurrentGlobalOrNull, GetNonCCWObjectGlobal};
|
||||
use js::jsapi::{HandleObject, Heap};
|
||||
use js::jsapi::{JSAutoRealm, JSContext};
|
||||
use js::jsval::UndefinedValue;
|
||||
use js::panic::maybe_resume_unwind;
|
||||
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::PipelineId;
|
||||
use msg::constellation_msg::{MessagePortId, MessagePortRouterId, PipelineId};
|
||||
use net_traits::image_cache::ImageCache;
|
||||
use net_traits::{CoreResourceThread, IpcSend, ResourceThreads};
|
||||
use profile_traits::{mem as profile_mem, time as profile_time};
|
||||
use script_traits::{MsDuration, ScriptToConstellationChan, TimerEvent};
|
||||
use script_traits::transferable::MessagePortImpl;
|
||||
use script_traits::{
|
||||
MessagePortMsg, MsDuration, PortMessageTask, ScriptMsg, ScriptToConstellationChan, TimerEvent,
|
||||
};
|
||||
use script_traits::{TimerEventId, TimerSchedulerMsg, TimerSource};
|
||||
use servo_url::{MutableOrigin, ServoUrl};
|
||||
use std::borrow::Cow;
|
||||
use std::cell::Cell;
|
||||
use std::collections::hash_map::Entry;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::{HashMap, VecDeque};
|
||||
use std::ffi::CString;
|
||||
use std::rc::Rc;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
|
@ -84,6 +95,9 @@ pub struct GlobalScope {
|
|||
crypto: MutNullableDom<Crypto>,
|
||||
next_worker_id: Cell<WorkerId>,
|
||||
|
||||
/// The message-port router id for this global, if it is managing ports.
|
||||
message_port_state: DomRefCell<MessagePortState>,
|
||||
|
||||
/// Pipeline id associated with this global.
|
||||
pipeline_id: PipelineId,
|
||||
|
||||
|
@ -167,6 +181,77 @@ pub struct GlobalScope {
|
|||
user_agent: Cow<'static, str>,
|
||||
}
|
||||
|
||||
/// A wrapper for glue-code between the ipc router and the event-loop.
|
||||
struct MessageListener {
|
||||
canceller: TaskCanceller,
|
||||
task_source: PortMessageQueue,
|
||||
context: Trusted<GlobalScope>,
|
||||
}
|
||||
|
||||
/// Data representing a message-port managed by this global.
|
||||
#[derive(JSTraceable, MallocSizeOf)]
|
||||
pub enum ManagedMessagePort {
|
||||
/// We keep ports pending when they are first transfer-received,
|
||||
/// and only add them, and ask the constellation to complete the transfer,
|
||||
/// in a subsequent task if the port hasn't been re-transfered.
|
||||
Pending(MessagePortImpl, WeakRef<MessagePort>),
|
||||
/// A port who was transferred into, or initially created in, this realm,
|
||||
/// and that hasn't been re-transferred in the same task it was noted.
|
||||
Added(MessagePortImpl, WeakRef<MessagePort>),
|
||||
}
|
||||
|
||||
/// State representing whether this global is currently managing messageports.
|
||||
#[derive(JSTraceable, MallocSizeOf)]
|
||||
pub enum MessagePortState {
|
||||
/// The message-port router id for this global, and a map of managed ports.
|
||||
Managed(
|
||||
MessagePortRouterId,
|
||||
HashMap<MessagePortId, ManagedMessagePort>,
|
||||
),
|
||||
/// This global is not managing any ports at this time.
|
||||
UnManaged,
|
||||
}
|
||||
|
||||
impl MessageListener {
|
||||
/// A new message came in, handle it via a task enqueued on the event-loop.
|
||||
/// A task is required, since we are using a trusted globalscope,
|
||||
/// and we can only access the root from the event-loop.
|
||||
fn notify(&self, msg: MessagePortMsg) {
|
||||
match msg {
|
||||
MessagePortMsg::CompleteTransfer(port_id, tasks) => {
|
||||
let context = self.context.clone();
|
||||
let _ = self.task_source.queue_with_canceller(
|
||||
task!(process_complete_transfer: move || {
|
||||
let global = context.root();
|
||||
global.complete_port_transfer(port_id, tasks);
|
||||
}),
|
||||
&self.canceller,
|
||||
);
|
||||
},
|
||||
MessagePortMsg::NewTask(port_id, task) => {
|
||||
let context = self.context.clone();
|
||||
let _ = self.task_source.queue_with_canceller(
|
||||
task!(process_new_task: move || {
|
||||
let global = context.root();
|
||||
global.route_task_to_port(port_id, task);
|
||||
}),
|
||||
&self.canceller,
|
||||
);
|
||||
},
|
||||
MessagePortMsg::RemoveMessagePort(port_id) => {
|
||||
let context = self.context.clone();
|
||||
let _ = self.task_source.queue_with_canceller(
|
||||
task!(process_remove_message_port: move || {
|
||||
let global = context.root();
|
||||
global.remove_message_port(&port_id);
|
||||
}),
|
||||
&self.canceller,
|
||||
);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl GlobalScope {
|
||||
pub fn new_inherited(
|
||||
pipeline_id: PipelineId,
|
||||
|
@ -183,6 +268,7 @@ impl GlobalScope {
|
|||
user_agent: Cow<'static, str>,
|
||||
) -> Self {
|
||||
Self {
|
||||
message_port_state: DomRefCell::new(MessagePortState::UnManaged),
|
||||
eventtarget: EventTarget::new_inherited(),
|
||||
crypto: Default::default(),
|
||||
next_worker_id: Cell::new(WorkerId(0)),
|
||||
|
@ -208,6 +294,397 @@ impl GlobalScope {
|
|||
}
|
||||
}
|
||||
|
||||
/// Complete the transfer of a message-port.
|
||||
fn complete_port_transfer(&self, port_id: MessagePortId, tasks: VecDeque<PortMessageTask>) {
|
||||
let should_start = if let MessagePortState::Managed(_id, message_ports) =
|
||||
&mut *self.message_port_state.borrow_mut()
|
||||
{
|
||||
match message_ports.get_mut(&port_id) {
|
||||
None => {
|
||||
panic!("CompleteTransfer msg received in a global not managing the port.");
|
||||
},
|
||||
Some(ManagedMessagePort::Pending(_, _)) => {
|
||||
panic!("CompleteTransfer msg received for a pending port.");
|
||||
},
|
||||
Some(ManagedMessagePort::Added(port_impl, _port)) => {
|
||||
port_impl.complete_transfer(tasks);
|
||||
port_impl.enabled()
|
||||
},
|
||||
}
|
||||
} else {
|
||||
return warn!("CompleteTransfer msg received in a global not managing any ports.");
|
||||
};
|
||||
if should_start {
|
||||
self.start_message_port(&port_id);
|
||||
}
|
||||
}
|
||||
|
||||
/// 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) {
|
||||
if let MessagePortState::Managed(router_id, _message_ports) =
|
||||
&*self.message_port_state.borrow()
|
||||
{
|
||||
let _ = self
|
||||
.script_to_constellation_chan()
|
||||
.send(ScriptMsg::RemoveMessagePortRouter(router_id.clone()));
|
||||
}
|
||||
*self.message_port_state.borrow_mut() = MessagePortState::UnManaged;
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#entangle>
|
||||
pub fn entangle_ports(&self, port1: MessagePortId, port2: MessagePortId) {
|
||||
if let MessagePortState::Managed(_id, message_ports) =
|
||||
&mut *self.message_port_state.borrow_mut()
|
||||
{
|
||||
for (port_id, entangled_id) in &[(port1, port2), (port2, port1)] {
|
||||
match message_ports.get_mut(&port_id) {
|
||||
None => {
|
||||
return warn!("entangled_ports called on a global not managing the port.");
|
||||
},
|
||||
Some(ManagedMessagePort::Pending(port_impl, dom_port)) => {
|
||||
dom_port
|
||||
.root()
|
||||
.expect("Port to be entangled to not have been GC'ed")
|
||||
.entangle(entangled_id.clone());
|
||||
port_impl.entangle(entangled_id.clone());
|
||||
},
|
||||
Some(ManagedMessagePort::Added(port_impl, dom_port)) => {
|
||||
dom_port
|
||||
.root()
|
||||
.expect("Port to be entangled to not have been GC'ed")
|
||||
.entangle(entangled_id.clone());
|
||||
port_impl.entangle(entangled_id.clone());
|
||||
},
|
||||
}
|
||||
}
|
||||
} else {
|
||||
panic!("entangled_ports called on a global not managing any ports.");
|
||||
}
|
||||
|
||||
let _ = self
|
||||
.script_to_constellation_chan()
|
||||
.send(ScriptMsg::EntanglePorts(port1, port2));
|
||||
}
|
||||
|
||||
/// Remove all referrences to a port.
|
||||
pub fn remove_message_port(&self, port_id: &MessagePortId) {
|
||||
let is_empty = if let MessagePortState::Managed(_id, message_ports) =
|
||||
&mut *self.message_port_state.borrow_mut()
|
||||
{
|
||||
match message_ports.remove(&port_id) {
|
||||
None => panic!("remove_message_port called on a global not managing the port."),
|
||||
Some(_) => message_ports.is_empty(),
|
||||
}
|
||||
} else {
|
||||
return warn!("remove_message_port called on a global not managing any ports.");
|
||||
};
|
||||
if is_empty {
|
||||
// Remove our port router,
|
||||
// it will be setup again if we start managing ports again.
|
||||
self.remove_message_ports_router();
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle the transfer of a port in the current task.
|
||||
pub fn mark_port_as_transferred(&self, port_id: &MessagePortId) -> MessagePortImpl {
|
||||
if let MessagePortState::Managed(_id, message_ports) =
|
||||
&mut *self.message_port_state.borrow_mut()
|
||||
{
|
||||
let mut port = match message_ports.remove(&port_id) {
|
||||
None => {
|
||||
panic!("mark_port_as_transferred called on a global not managing the port.")
|
||||
},
|
||||
Some(ManagedMessagePort::Pending(port_impl, _)) => port_impl,
|
||||
Some(ManagedMessagePort::Added(port_impl, _)) => port_impl,
|
||||
};
|
||||
port.set_has_been_shipped();
|
||||
let _ = self
|
||||
.script_to_constellation_chan()
|
||||
.send(ScriptMsg::MessagePortShipped(port_id.clone()));
|
||||
port
|
||||
} else {
|
||||
panic!("mark_port_as_transferred called on a global not managing any ports.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#dom-messageport-start>
|
||||
pub fn start_message_port(&self, port_id: &MessagePortId) {
|
||||
if let MessagePortState::Managed(_id, message_ports) =
|
||||
&mut *self.message_port_state.borrow_mut()
|
||||
{
|
||||
let port = match message_ports.get_mut(&port_id) {
|
||||
None => panic!("start_message_port called on a unknown port."),
|
||||
Some(ManagedMessagePort::Pending(port_impl, _)) => port_impl,
|
||||
Some(ManagedMessagePort::Added(port_impl, _)) => port_impl,
|
||||
};
|
||||
if let Some(message_buffer) = port.start() {
|
||||
for task in message_buffer {
|
||||
let port_id = port_id.clone();
|
||||
let this = Trusted::new(&*self);
|
||||
let _ = self.port_message_queue().queue(
|
||||
task!(process_pending_port_messages: move || {
|
||||
let target_global = this.root();
|
||||
target_global.route_task_to_port(port_id, task);
|
||||
}),
|
||||
&self,
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return warn!("start_message_port called on a global not managing any ports.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#dom-messageport-close>
|
||||
pub fn close_message_port(&self, port_id: &MessagePortId) {
|
||||
if let MessagePortState::Managed(_id, message_ports) =
|
||||
&mut *self.message_port_state.borrow_mut()
|
||||
{
|
||||
let port = match message_ports.get_mut(&port_id) {
|
||||
None => panic!("close_message_port called on an unknown port."),
|
||||
Some(ManagedMessagePort::Pending(port_impl, _)) => port_impl,
|
||||
Some(ManagedMessagePort::Added(port_impl, _)) => port_impl,
|
||||
};
|
||||
port.close();
|
||||
} else {
|
||||
return warn!("close_message_port called on a global not managing any ports.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#message-port-post-message-steps>
|
||||
// Steps 6 and 7
|
||||
pub fn post_messageport_msg(&self, port_id: MessagePortId, task: PortMessageTask) {
|
||||
if let MessagePortState::Managed(_id, message_ports) =
|
||||
&mut *self.message_port_state.borrow_mut()
|
||||
{
|
||||
let port = match message_ports.get_mut(&port_id) {
|
||||
None => panic!("post_messageport_msg called on an unknown port."),
|
||||
Some(ManagedMessagePort::Pending(port_impl, _)) => port_impl,
|
||||
Some(ManagedMessagePort::Added(port_impl, _)) => port_impl,
|
||||
};
|
||||
if let Some(entangled_id) = port.entangled_port_id() {
|
||||
// Step 7
|
||||
let this = Trusted::new(&*self);
|
||||
let _ = self.port_message_queue().queue(
|
||||
task!(post_message: move || {
|
||||
let global = this.root();
|
||||
// Note: we do this in a task, as this will ensure the global and constellation
|
||||
// are aware of any transfer that might still take place in the current task.
|
||||
global.route_task_to_port(entangled_id, task);
|
||||
}),
|
||||
self,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
return warn!("post_messageport_msg called on a global not managing any ports.");
|
||||
}
|
||||
}
|
||||
|
||||
/// If we don't know about the port,
|
||||
/// send the message to the constellation for routing.
|
||||
fn re_route_port_task(&self, port_id: MessagePortId, task: PortMessageTask) {
|
||||
let _ = self
|
||||
.script_to_constellation_chan()
|
||||
.send(ScriptMsg::RerouteMessagePort(port_id, task));
|
||||
}
|
||||
|
||||
/// Route the task to be handled by the relevant port.
|
||||
pub fn route_task_to_port(&self, port_id: MessagePortId, task: PortMessageTask) {
|
||||
let should_dispatch = if let MessagePortState::Managed(_id, message_ports) =
|
||||
&mut *self.message_port_state.borrow_mut()
|
||||
{
|
||||
if !message_ports.contains_key(&port_id) {
|
||||
self.re_route_port_task(port_id, task);
|
||||
return;
|
||||
}
|
||||
let (port_impl, dom_port) = match message_ports.get_mut(&port_id) {
|
||||
None => panic!("route_task_to_port called for an unknown port."),
|
||||
Some(ManagedMessagePort::Pending(port_impl, dom_port)) => (port_impl, dom_port),
|
||||
Some(ManagedMessagePort::Added(port_impl, dom_port)) => (port_impl, dom_port),
|
||||
};
|
||||
|
||||
// If the port is not enabled yet, or if is awaiting the completion of it's transfer,
|
||||
// the task will be buffered and dispatched upon enablement or completion of the transfer.
|
||||
if let Some(task_to_dispatch) = port_impl.handle_incoming(task) {
|
||||
// Get a corresponding DOM message-port object.
|
||||
let dom_port = match dom_port.root() {
|
||||
Some(dom_port) => dom_port,
|
||||
None => panic!("Messageport Gc'ed too early"),
|
||||
};
|
||||
Some((dom_port, task_to_dispatch))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
self.re_route_port_task(port_id, task);
|
||||
return;
|
||||
};
|
||||
if let Some((dom_port, PortMessageTask { origin, data })) = should_dispatch {
|
||||
// Substep 3-4
|
||||
rooted!(in(*self.get_cx()) let mut message_clone = UndefinedValue());
|
||||
if let Ok(ports) = structuredclone::read(self, data, message_clone.handle_mut()) {
|
||||
// Substep 6
|
||||
// Dispatch the event, using the dom message-port.
|
||||
MessageEvent::dispatch_jsval(
|
||||
&dom_port.upcast(),
|
||||
self,
|
||||
message_clone.handle(),
|
||||
Some(&origin.ascii_serialization()),
|
||||
None,
|
||||
ports,
|
||||
);
|
||||
} else {
|
||||
// Step 4, fire messageerror event.
|
||||
MessageEvent::dispatch_error(&dom_port.upcast(), self);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Check all ports that have been transfer-received in the previous task,
|
||||
/// and complete their transfer if they haven't been re-transferred.
|
||||
pub fn maybe_add_pending_ports(&self) {
|
||||
if let MessagePortState::Managed(router_id, message_ports) =
|
||||
&mut *self.message_port_state.borrow_mut()
|
||||
{
|
||||
let to_be_added: Vec<MessagePortId> = message_ports
|
||||
.iter()
|
||||
.filter_map(|(id, port_info)| match port_info {
|
||||
ManagedMessagePort::Pending(_, _) => Some(id.clone()),
|
||||
_ => None,
|
||||
})
|
||||
.collect();
|
||||
for id in to_be_added {
|
||||
let (id, port_info) = message_ports
|
||||
.remove_entry(&id)
|
||||
.expect("Collected port-id to match an entry");
|
||||
if let ManagedMessagePort::Pending(port_impl, dom_port) = port_info {
|
||||
let _ = self
|
||||
.script_to_constellation_chan()
|
||||
.send(ScriptMsg::NewMessagePort(
|
||||
router_id.clone(),
|
||||
port_impl.message_port_id().clone(),
|
||||
));
|
||||
let new_port_info = ManagedMessagePort::Added(port_impl, dom_port);
|
||||
let present = message_ports.insert(id, new_port_info);
|
||||
assert!(present.is_none());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
warn!("maybe_add_pending_ports called on a global not managing any ports.");
|
||||
}
|
||||
}
|
||||
|
||||
/// https://html.spec.whatwg.org/multipage/#ports-and-garbage-collection
|
||||
pub fn perform_a_message_port_garbage_collection_checkpoint(&self) {
|
||||
let is_empty = if let MessagePortState::Managed(_id, message_ports) =
|
||||
&mut *self.message_port_state.borrow_mut()
|
||||
{
|
||||
let to_be_removed: Vec<MessagePortId> = message_ports
|
||||
.iter()
|
||||
.filter_map(|(id, port_info)| {
|
||||
if let ManagedMessagePort::Added(_port_impl, dom_port) = port_info {
|
||||
if dom_port.root().is_none() {
|
||||
// Let the constellation know to drop this port and the one it is entangled with,
|
||||
// and to forward this message to the script-process where the entangled is found.
|
||||
let _ = self
|
||||
.script_to_constellation_chan()
|
||||
.send(ScriptMsg::RemoveMessagePort(id.clone()));
|
||||
return Some(id.clone());
|
||||
}
|
||||
}
|
||||
None
|
||||
})
|
||||
.collect();
|
||||
for id in to_be_removed {
|
||||
message_ports.remove(&id);
|
||||
}
|
||||
message_ports.is_empty()
|
||||
} else {
|
||||
false
|
||||
};
|
||||
if is_empty {
|
||||
self.remove_message_ports_router();
|
||||
}
|
||||
}
|
||||
|
||||
/// Start tracking a message-port
|
||||
pub fn track_message_port(&self, dom_port: &MessagePort, port_impl: Option<MessagePortImpl>) {
|
||||
let mut current_state = self.message_port_state.borrow_mut();
|
||||
|
||||
if let MessagePortState::UnManaged = &*current_state {
|
||||
// Setup a route for IPC, for messages from the constellation to our ports.
|
||||
let (port_control_sender, port_control_receiver) =
|
||||
ipc::channel().expect("ipc channel failure");
|
||||
let context = Trusted::new(self);
|
||||
let (task_source, canceller) = (
|
||||
self.port_message_queue(),
|
||||
self.task_canceller(TaskSourceName::PortMessage),
|
||||
);
|
||||
let listener = MessageListener {
|
||||
canceller,
|
||||
task_source,
|
||||
context,
|
||||
};
|
||||
ROUTER.add_route(
|
||||
port_control_receiver.to_opaque(),
|
||||
Box::new(move |message| {
|
||||
let msg = message.to();
|
||||
match msg {
|
||||
Ok(msg) => listener.notify(msg),
|
||||
Err(err) => warn!("Error receiving a MessagePortMsg: {:?}", err),
|
||||
}
|
||||
}),
|
||||
);
|
||||
let router_id = MessagePortRouterId::new();
|
||||
*current_state = MessagePortState::Managed(router_id.clone(), HashMap::new());
|
||||
let _ = self
|
||||
.script_to_constellation_chan()
|
||||
.send(ScriptMsg::NewMessagePortRouter(
|
||||
router_id,
|
||||
port_control_sender,
|
||||
));
|
||||
}
|
||||
|
||||
if let MessagePortState::Managed(router_id, message_ports) = &mut *current_state {
|
||||
if let Some(port_impl) = port_impl {
|
||||
// We keep transfer-received ports as "pending",
|
||||
// and only ask the constellation to complete the transfer
|
||||
// if they're not re-shipped in the current task.
|
||||
message_ports.insert(
|
||||
dom_port.message_port_id().clone(),
|
||||
ManagedMessagePort::Pending(port_impl, WeakRef::new(dom_port)),
|
||||
);
|
||||
|
||||
// Queue a task to complete the transfer,
|
||||
// unless the port is re-transferred in the current task.
|
||||
let this = Trusted::new(&*self);
|
||||
let _ = self.port_message_queue().queue(
|
||||
task!(process_pending_port_messages: move || {
|
||||
let target_global = this.root();
|
||||
target_global.maybe_add_pending_ports();
|
||||
}),
|
||||
&self,
|
||||
);
|
||||
} else {
|
||||
// If this is a newly-created port, let the constellation immediately know.
|
||||
let port_impl = MessagePortImpl::new(dom_port.message_port_id().clone());
|
||||
message_ports.insert(
|
||||
dom_port.message_port_id().clone(),
|
||||
ManagedMessagePort::Added(port_impl, WeakRef::new(dom_port)),
|
||||
);
|
||||
let _ = self
|
||||
.script_to_constellation_chan()
|
||||
.send(ScriptMsg::NewMessagePort(
|
||||
router_id.clone(),
|
||||
dom_port.message_port_id().clone(),
|
||||
));
|
||||
};
|
||||
} else {
|
||||
panic!("track_message_port should have first switched the state to managed.");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn track_worker(&self, closing_worker: Arc<AtomicBool>) {
|
||||
self.list_auto_close_worker
|
||||
.borrow_mut()
|
||||
|
@ -504,7 +981,7 @@ impl GlobalScope {
|
|||
unreachable!();
|
||||
}
|
||||
|
||||
/// `ScriptChan` to send messages to the networking task source of
|
||||
/// `TaskSource` to send messages to the networking task source of
|
||||
/// this global scope.
|
||||
pub fn networking_task_source(&self) -> NetworkingTaskSource {
|
||||
if let Some(window) = self.downcast::<Window>() {
|
||||
|
@ -516,7 +993,19 @@ impl GlobalScope {
|
|||
unreachable!();
|
||||
}
|
||||
|
||||
/// `ScriptChan` to send messages to the remote-event task source of
|
||||
/// `TaskSource` to send messages to the port message queue of
|
||||
/// this global scope.
|
||||
pub fn port_message_queue(&self) -> PortMessageQueue {
|
||||
if let Some(window) = self.downcast::<Window>() {
|
||||
return window.task_manager().port_message_queue();
|
||||
}
|
||||
if let Some(worker) = self.downcast::<WorkerGlobalScope>() {
|
||||
return worker.port_message_queue();
|
||||
}
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
/// `TaskSource` to send messages to the remote-event task source of
|
||||
/// this global scope.
|
||||
pub fn remote_event_task_source(&self) -> RemoteEventTaskSource {
|
||||
if let Some(window) = self.downcast::<Window>() {
|
||||
|
@ -528,7 +1017,7 @@ impl GlobalScope {
|
|||
unreachable!();
|
||||
}
|
||||
|
||||
/// `ScriptChan` to send messages to the websocket task source of
|
||||
/// `TaskSource` to send messages to the websocket task source of
|
||||
/// this global scope.
|
||||
pub fn websocket_task_source(&self) -> WebsocketTaskSource {
|
||||
if let Some(window) = self.downcast::<Window>() {
|
||||
|
|
|
@ -11,7 +11,7 @@ use crate::dom::bindings::inheritance::Castable;
|
|||
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector};
|
||||
use crate::dom::bindings::root::{Dom, DomRoot};
|
||||
use crate::dom::bindings::str::{DOMString, USVString};
|
||||
use crate::dom::bindings::structuredclone::StructuredCloneData;
|
||||
use crate::dom::bindings::structuredclone;
|
||||
use crate::dom::event::Event;
|
||||
use crate::dom::eventtarget::EventTarget;
|
||||
use crate::dom::globalscope::GlobalScope;
|
||||
|
@ -27,7 +27,7 @@ use msg::constellation_msg::{HistoryStateId, TraversalDirection};
|
|||
use net_traits::{CoreResourceMsg, IpcSend};
|
||||
use profile_traits::ipc;
|
||||
use profile_traits::ipc::channel;
|
||||
use script_traits::ScriptMsg;
|
||||
use script_traits::{ScriptMsg, StructuredSerializedData};
|
||||
use servo_url::ServoUrl;
|
||||
use std::cell::Cell;
|
||||
|
||||
|
@ -115,11 +115,16 @@ impl History {
|
|||
};
|
||||
|
||||
match serialized_data {
|
||||
Some(serialized_data) => {
|
||||
Some(data) => {
|
||||
let data = StructuredSerializedData {
|
||||
serialized: data,
|
||||
ports: None,
|
||||
};
|
||||
let global_scope = self.window.upcast::<GlobalScope>();
|
||||
rooted!(in(*global_scope.get_cx()) let mut state = UndefinedValue());
|
||||
StructuredCloneData::Vector(serialized_data)
|
||||
.read(&global_scope, state.handle_mut());
|
||||
if let Err(_) = structuredclone::read(&global_scope, data, state.handle_mut()) {
|
||||
warn!("Error reading structuredclone data");
|
||||
}
|
||||
self.state.set(state.get());
|
||||
},
|
||||
None => {
|
||||
|
@ -185,7 +190,7 @@ impl History {
|
|||
// TODO: Step 4
|
||||
|
||||
// Step 5
|
||||
let serialized_data = StructuredCloneData::write(*cx, data)?.move_to_arraybuffer();
|
||||
let serialized_data = structuredclone::write(cx, data, None)?;
|
||||
|
||||
let new_url: ServoUrl = match url {
|
||||
// Step 6
|
||||
|
@ -254,7 +259,7 @@ impl History {
|
|||
};
|
||||
|
||||
let _ = self.window.upcast::<GlobalScope>().resource_threads().send(
|
||||
CoreResourceMsg::SetHistoryState(state_id, serialized_data.clone()),
|
||||
CoreResourceMsg::SetHistoryState(state_id, serialized_data.serialized.clone()),
|
||||
);
|
||||
|
||||
// TODO: Step 9 Update current entry to represent a GET request
|
||||
|
@ -266,7 +271,9 @@ impl History {
|
|||
// Step 11
|
||||
let global_scope = self.window.upcast::<GlobalScope>();
|
||||
rooted!(in(*cx) let mut state = UndefinedValue());
|
||||
StructuredCloneData::Vector(serialized_data).read(&global_scope, state.handle_mut());
|
||||
if let Err(_) = structuredclone::read(&global_scope, serialized_data, state.handle_mut()) {
|
||||
warn!("Error reading structuredclone data");
|
||||
}
|
||||
|
||||
// Step 12
|
||||
self.state.set(state.get());
|
||||
|
|
66
components/script/dom/messagechannel.rs
Normal file
66
components/script/dom/messagechannel.rs
Normal file
|
@ -0,0 +1,66 @@
|
|||
/* 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/. */
|
||||
|
||||
use crate::dom::bindings::codegen::Bindings::MessageChannelBinding::{MessageChannelMethods, Wrap};
|
||||
use crate::dom::bindings::error::{Error, Fallible};
|
||||
use crate::dom::bindings::reflector::{reflect_dom_object, Reflector};
|
||||
use crate::dom::bindings::root::{Dom, DomRoot};
|
||||
use crate::dom::globalscope::GlobalScope;
|
||||
use crate::dom::messageport::MessagePort;
|
||||
use dom_struct::dom_struct;
|
||||
|
||||
#[dom_struct]
|
||||
pub struct MessageChannel {
|
||||
reflector_: Reflector,
|
||||
port1: Dom<MessagePort>,
|
||||
port2: Dom<MessagePort>,
|
||||
}
|
||||
|
||||
impl MessageChannel {
|
||||
/// <https://html.spec.whatwg.org/multipage/#dom-messagechannel>
|
||||
pub fn Constructor(global: &GlobalScope) -> Fallible<DomRoot<MessageChannel>> {
|
||||
let incumbent = GlobalScope::incumbent().ok_or(Error::InvalidState)?;
|
||||
|
||||
// Step 1
|
||||
let port1 = MessagePort::new(&incumbent);
|
||||
|
||||
// Step 2
|
||||
let port2 = MessagePort::new(&incumbent);
|
||||
|
||||
incumbent.track_message_port(&*port1, None);
|
||||
incumbent.track_message_port(&*port2, None);
|
||||
|
||||
// Step 3
|
||||
incumbent.entangle_ports(
|
||||
port1.message_port_id().clone(),
|
||||
port2.message_port_id().clone(),
|
||||
);
|
||||
|
||||
// Steps 4-6
|
||||
let channel = reflect_dom_object(
|
||||
Box::new(MessageChannel {
|
||||
reflector_: Reflector::new(),
|
||||
port1: Dom::from_ref(&port1),
|
||||
port2: Dom::from_ref(&port2),
|
||||
}),
|
||||
global,
|
||||
Wrap,
|
||||
);
|
||||
|
||||
// Step 7
|
||||
Ok(channel)
|
||||
}
|
||||
}
|
||||
|
||||
impl MessageChannelMethods for MessageChannel {
|
||||
/// <https://html.spec.whatwg.org/multipage/#dom-messagechannel-port1>
|
||||
fn Port1(&self) -> DomRoot<MessagePort> {
|
||||
DomRoot::from_ref(&*self.port1)
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#dom-messagechannel-port2>
|
||||
fn Port2(&self) -> DomRoot<MessagePort> {
|
||||
DomRoot::from_ref(&*self.port2)
|
||||
}
|
||||
}
|
|
@ -11,9 +11,11 @@ use crate::dom::bindings::reflector::{reflect_dom_object, DomObject};
|
|||
use crate::dom::bindings::root::{Dom, DomRoot};
|
||||
use crate::dom::bindings::str::DOMString;
|
||||
use crate::dom::bindings::trace::RootedTraceableBox;
|
||||
use crate::dom::bindings::utils::message_ports_to_frozen_array;
|
||||
use crate::dom::event::Event;
|
||||
use crate::dom::eventtarget::EventTarget;
|
||||
use crate::dom::globalscope::GlobalScope;
|
||||
use crate::dom::messageport::MessagePort;
|
||||
use crate::dom::windowproxy::WindowProxy;
|
||||
use crate::script_runtime::JSContext;
|
||||
use dom_struct::dom_struct;
|
||||
|
@ -31,6 +33,7 @@ pub struct MessageEvent {
|
|||
origin: DOMString,
|
||||
source: Option<Dom<WindowProxy>>,
|
||||
lastEventId: DOMString,
|
||||
ports: Vec<DomRoot<MessagePort>>,
|
||||
}
|
||||
|
||||
impl MessageEvent {
|
||||
|
@ -41,6 +44,7 @@ impl MessageEvent {
|
|||
DOMString::new(),
|
||||
None,
|
||||
DOMString::new(),
|
||||
vec![],
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -50,13 +54,15 @@ impl MessageEvent {
|
|||
origin: DOMString,
|
||||
source: Option<&WindowProxy>,
|
||||
lastEventId: DOMString,
|
||||
ports: Vec<DomRoot<MessagePort>>,
|
||||
) -> DomRoot<MessageEvent> {
|
||||
let ev = Box::new(MessageEvent {
|
||||
event: Event::new_inherited(),
|
||||
data: Heap::default(),
|
||||
origin: origin,
|
||||
source: source.map(Dom::from_ref),
|
||||
lastEventId: lastEventId,
|
||||
origin,
|
||||
lastEventId,
|
||||
ports,
|
||||
});
|
||||
let ev = reflect_dom_object(ev, global, MessageEventBinding::Wrap);
|
||||
ev.data.set(data.get());
|
||||
|
@ -73,8 +79,9 @@ impl MessageEvent {
|
|||
origin: DOMString,
|
||||
source: Option<&WindowProxy>,
|
||||
lastEventId: DOMString,
|
||||
ports: Vec<DomRoot<MessagePort>>,
|
||||
) -> DomRoot<MessageEvent> {
|
||||
let ev = MessageEvent::new_initialized(global, data, origin, source, lastEventId);
|
||||
let ev = MessageEvent::new_initialized(global, data, origin, source, lastEventId, ports);
|
||||
{
|
||||
let event = ev.upcast::<Event>();
|
||||
event.init_event(type_, bubbles, cancelable);
|
||||
|
@ -100,6 +107,7 @@ impl MessageEvent {
|
|||
init.origin.clone(),
|
||||
source.as_ref().map(|source| &**source),
|
||||
init.lastEventId.clone(),
|
||||
init.ports.clone().unwrap_or(vec![]),
|
||||
);
|
||||
Ok(ev)
|
||||
}
|
||||
|
@ -112,6 +120,7 @@ impl MessageEvent {
|
|||
message: HandleValue,
|
||||
origin: Option<&str>,
|
||||
source: Option<&WindowProxy>,
|
||||
ports: Vec<DomRoot<MessagePort>>,
|
||||
) {
|
||||
let messageevent = MessageEvent::new(
|
||||
scope,
|
||||
|
@ -122,18 +131,39 @@ impl MessageEvent {
|
|||
DOMString::from(origin.unwrap_or("")),
|
||||
source,
|
||||
DOMString::new(),
|
||||
ports,
|
||||
);
|
||||
messageevent.upcast::<Event>().fire(target);
|
||||
}
|
||||
|
||||
pub fn dispatch_error(target: &EventTarget, scope: &GlobalScope) {
|
||||
let init = MessageEventBinding::MessageEventInit::empty();
|
||||
let source = init
|
||||
.source
|
||||
.as_ref()
|
||||
.and_then(|inner| inner.as_ref().map(|source| source.window_proxy()));
|
||||
let messageevent = MessageEvent::new(
|
||||
scope,
|
||||
atom!("messageerror"),
|
||||
init.parent.bubbles,
|
||||
init.parent.cancelable,
|
||||
init.data.handle(),
|
||||
init.origin.clone(),
|
||||
source.as_ref().map(|source| &**source),
|
||||
init.lastEventId.clone(),
|
||||
init.ports.clone().unwrap_or(vec![]),
|
||||
);
|
||||
messageevent.upcast::<Event>().fire(target);
|
||||
}
|
||||
}
|
||||
|
||||
impl MessageEventMethods for MessageEvent {
|
||||
// https://html.spec.whatwg.org/multipage/#dom-messageevent-data
|
||||
/// <https://html.spec.whatwg.org/multipage/#dom-messageevent-data>
|
||||
fn Data(&self, _cx: JSContext) -> JSVal {
|
||||
self.data.get()
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/#dom-messageevent-origin
|
||||
/// <https://html.spec.whatwg.org/multipage/#dom-messageevent-origin>
|
||||
fn Origin(&self) -> DOMString {
|
||||
self.origin.clone()
|
||||
}
|
||||
|
@ -145,13 +175,18 @@ impl MessageEventMethods for MessageEvent {
|
|||
.and_then(|source| NonNull::new(source.reflector().get_jsobject().get()))
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/#dom-messageevent-lasteventid
|
||||
/// <https://html.spec.whatwg.org/multipage/#dom-messageevent-lasteventid>
|
||||
fn LastEventId(&self) -> DOMString {
|
||||
self.lastEventId.clone()
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-event-istrusted
|
||||
/// <https://dom.spec.whatwg.org/#dom-event-istrusted>
|
||||
fn IsTrusted(&self) -> bool {
|
||||
self.event.IsTrusted()
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#dom-messageevent-ports>
|
||||
fn Ports(&self, cx: JSContext) -> JSVal {
|
||||
message_ports_to_frozen_array(self.ports.as_slice(), cx)
|
||||
}
|
||||
}
|
||||
|
|
345
components/script/dom/messageport.rs
Normal file
345
components/script/dom/messageport.rs
Normal file
|
@ -0,0 +1,345 @@
|
|||
/* 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/. */
|
||||
|
||||
use crate::dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull;
|
||||
use crate::dom::bindings::codegen::Bindings::MessagePortBinding::{
|
||||
MessagePortMethods, PostMessageOptions, Wrap,
|
||||
};
|
||||
use crate::dom::bindings::conversions::root_from_object;
|
||||
use crate::dom::bindings::error::{Error, ErrorResult};
|
||||
use crate::dom::bindings::inheritance::Castable;
|
||||
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject};
|
||||
use crate::dom::bindings::root::DomRoot;
|
||||
use crate::dom::bindings::structuredclone::{self, StructuredDataHolder};
|
||||
use crate::dom::bindings::trace::RootedTraceableBox;
|
||||
use crate::dom::bindings::transferable::Transferable;
|
||||
use crate::dom::eventtarget::EventTarget;
|
||||
use crate::dom::globalscope::GlobalScope;
|
||||
use crate::script_runtime::JSContext as SafeJSContext;
|
||||
use dom_struct::dom_struct;
|
||||
use js::jsapi::Heap;
|
||||
use js::jsapi::{JSObject, MutableHandleObject};
|
||||
use js::rust::{CustomAutoRooter, CustomAutoRooterGuard, HandleValue};
|
||||
use msg::constellation_msg::{MessagePortId, MessagePortIndex, PipelineNamespaceId};
|
||||
use script_traits::PortMessageTask;
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::collections::HashMap;
|
||||
use std::convert::TryInto;
|
||||
use std::num::NonZeroU32;
|
||||
use std::rc::Rc;
|
||||
|
||||
#[dom_struct]
|
||||
/// The MessagePort used in the DOM.
|
||||
pub struct MessagePort {
|
||||
eventtarget: EventTarget,
|
||||
message_port_id: MessagePortId,
|
||||
entangled_port: RefCell<Option<MessagePortId>>,
|
||||
detached: Cell<bool>,
|
||||
}
|
||||
|
||||
impl MessagePort {
|
||||
fn new_inherited(message_port_id: MessagePortId) -> MessagePort {
|
||||
MessagePort {
|
||||
eventtarget: EventTarget::new_inherited(),
|
||||
entangled_port: RefCell::new(None),
|
||||
detached: Cell::new(false),
|
||||
message_port_id,
|
||||
}
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#create-a-new-messageport-object>
|
||||
pub fn new(owner: &GlobalScope) -> DomRoot<MessagePort> {
|
||||
let port_id = MessagePortId::new();
|
||||
reflect_dom_object(Box::new(MessagePort::new_inherited(port_id)), owner, Wrap)
|
||||
}
|
||||
|
||||
/// Create a new port for an incoming transfer-received one.
|
||||
fn new_transferred(
|
||||
owner: &GlobalScope,
|
||||
transferred_port: MessagePortId,
|
||||
entangled_port: Option<MessagePortId>,
|
||||
) -> DomRoot<MessagePort> {
|
||||
reflect_dom_object(
|
||||
Box::new(MessagePort {
|
||||
message_port_id: transferred_port,
|
||||
eventtarget: EventTarget::new_inherited(),
|
||||
detached: Cell::new(false),
|
||||
entangled_port: RefCell::new(entangled_port),
|
||||
}),
|
||||
owner,
|
||||
Wrap,
|
||||
)
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#entangle>
|
||||
pub fn entangle(&self, other_id: MessagePortId) {
|
||||
*self.entangled_port.borrow_mut() = Some(other_id);
|
||||
}
|
||||
|
||||
pub fn message_port_id(&self) -> &MessagePortId {
|
||||
&self.message_port_id
|
||||
}
|
||||
|
||||
pub fn detached(&self) -> bool {
|
||||
self.detached.get()
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#handler-messageport-onmessage>
|
||||
fn set_onmessage(&self, listener: Option<Rc<EventHandlerNonNull>>) {
|
||||
let eventtarget = self.upcast::<EventTarget>();
|
||||
eventtarget.set_event_handler_common("message", listener);
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#message-port-post-message-steps>
|
||||
fn post_message_impl(
|
||||
&self,
|
||||
cx: SafeJSContext,
|
||||
message: HandleValue,
|
||||
transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
|
||||
) -> ErrorResult {
|
||||
if self.detached.get() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Step 1 is the transfer argument.
|
||||
|
||||
let target_port = self.entangled_port.borrow();
|
||||
|
||||
// Step 3
|
||||
let mut doomed = false;
|
||||
|
||||
let ports = transfer
|
||||
.iter()
|
||||
.filter_map(|&obj| root_from_object::<MessagePort>(obj, *cx).ok());
|
||||
for port in ports {
|
||||
// Step 2
|
||||
if port.message_port_id() == self.message_port_id() {
|
||||
return Err(Error::DataClone);
|
||||
}
|
||||
|
||||
// Step 4
|
||||
if let Some(target_id) = target_port.as_ref() {
|
||||
if port.message_port_id() == target_id {
|
||||
doomed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Step 5
|
||||
let data = structuredclone::write(cx, message, Some(transfer))?;
|
||||
|
||||
if doomed {
|
||||
// TODO: The spec says to optionally report such a case to a dev console.
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Step 6, done in MessagePortImpl.
|
||||
|
||||
let incumbent = match GlobalScope::incumbent() {
|
||||
None => unreachable!("postMessage called with no incumbent global"),
|
||||
Some(incumbent) => incumbent,
|
||||
};
|
||||
|
||||
// Step 7
|
||||
let task = PortMessageTask {
|
||||
origin: incumbent.origin().immutable().clone(),
|
||||
data,
|
||||
};
|
||||
|
||||
// Have the global proxy this call to the corresponding MessagePortImpl.
|
||||
self.global()
|
||||
.post_messageport_msg(self.message_port_id().clone(), task);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Transferable for MessagePort {
|
||||
/// <https://html.spec.whatwg.org/multipage/#message-ports:transfer-steps>
|
||||
fn transfer(&self, sc_holder: &mut StructuredDataHolder) -> Result<u64, ()> {
|
||||
if self.detached.get() {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
let port_impls = match sc_holder {
|
||||
StructuredDataHolder::Write(port_impls) => port_impls,
|
||||
_ => panic!("Unexpected variant of StructuredDataHolder"),
|
||||
};
|
||||
|
||||
self.detached.set(true);
|
||||
let id = self.message_port_id();
|
||||
|
||||
// 1. Run local transfer logic, and return the object to be transferred.
|
||||
let transferred_port = self.global().mark_port_as_transferred(id);
|
||||
|
||||
// 2. Store the transferred object at a given key.
|
||||
if let Some(ports) = port_impls.as_mut() {
|
||||
ports.insert(id.clone(), transferred_port);
|
||||
} else {
|
||||
let mut ports = HashMap::new();
|
||||
ports.insert(id.clone(), transferred_port);
|
||||
*port_impls = Some(ports);
|
||||
}
|
||||
|
||||
let PipelineNamespaceId(name_space) = id.clone().namespace_id;
|
||||
let MessagePortIndex(index) = id.clone().index;
|
||||
let index = index.get();
|
||||
|
||||
let mut big: [u8; 8] = [0; 8];
|
||||
let name_space = name_space.to_ne_bytes();
|
||||
let index = index.to_ne_bytes();
|
||||
|
||||
let (left, right) = big.split_at_mut(4);
|
||||
left.copy_from_slice(&name_space);
|
||||
right.copy_from_slice(&index);
|
||||
|
||||
// 3. Return a u64 representation of the key where the object is stored.
|
||||
Ok(u64::from_ne_bytes(big))
|
||||
}
|
||||
|
||||
/// https://html.spec.whatwg.org/multipage/#message-ports:transfer-receiving-steps
|
||||
fn transfer_receive(
|
||||
owner: &DomRoot<GlobalScope>,
|
||||
sc_holder: &mut StructuredDataHolder,
|
||||
extra_data: u64,
|
||||
return_object: MutableHandleObject,
|
||||
) -> Result<(), ()> {
|
||||
let (message_ports, port_impls) = match sc_holder {
|
||||
StructuredDataHolder::Read {
|
||||
message_ports,
|
||||
port_impls,
|
||||
..
|
||||
} => (message_ports, port_impls),
|
||||
_ => panic!("Unexpected variant of StructuredDataHolder"),
|
||||
};
|
||||
|
||||
// 1. Re-build the key for the storage location
|
||||
// of the transferred object.
|
||||
let big: [u8; 8] = extra_data.to_ne_bytes();
|
||||
let (name_space, index) = big.split_at(4);
|
||||
|
||||
let namespace_id = PipelineNamespaceId(u32::from_ne_bytes(
|
||||
name_space
|
||||
.try_into()
|
||||
.expect("name_space to be a slice of four."),
|
||||
));
|
||||
let index = MessagePortIndex(
|
||||
NonZeroU32::new(u32::from_ne_bytes(
|
||||
index.try_into().expect("index to be a slice of four."),
|
||||
))
|
||||
.expect("Index to be non-zero"),
|
||||
);
|
||||
|
||||
let id = MessagePortId {
|
||||
namespace_id,
|
||||
index,
|
||||
};
|
||||
|
||||
// 2. Get the transferred object from its storage, using the key.
|
||||
// Assign the transfer-received port-impl, and total number of transferred ports.
|
||||
let (ports_len, port_impl) = if let Some(ports) = port_impls.as_mut() {
|
||||
let ports_len = ports.len();
|
||||
let port_impl = ports.remove(&id).expect("Transferred port to be stored");
|
||||
if ports.is_empty() {
|
||||
*port_impls = None;
|
||||
}
|
||||
(ports_len, port_impl)
|
||||
} else {
|
||||
panic!("A messageport was transfer-received, yet the SC holder does not have any port impls");
|
||||
};
|
||||
|
||||
let transferred_port =
|
||||
MessagePort::new_transferred(&**owner, id.clone(), port_impl.entangled_port_id());
|
||||
owner.track_message_port(&transferred_port, Some(port_impl));
|
||||
|
||||
return_object.set(transferred_port.reflector().rootable().get());
|
||||
|
||||
// Store the DOM port where it will be passed along to script in the message-event.
|
||||
if let Some(ports) = message_ports.as_mut() {
|
||||
ports.push(transferred_port);
|
||||
} else {
|
||||
let mut ports = Vec::with_capacity(ports_len);
|
||||
ports.push(transferred_port);
|
||||
*message_ports = Some(ports);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl MessagePortMethods for MessagePort {
|
||||
/// <https://html.spec.whatwg.org/multipage/#dom-messageport-postmessage>
|
||||
fn PostMessage(
|
||||
&self,
|
||||
cx: SafeJSContext,
|
||||
message: HandleValue,
|
||||
transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
|
||||
) -> ErrorResult {
|
||||
if self.detached.get() {
|
||||
return Ok(());
|
||||
}
|
||||
self.post_message_impl(cx, message, transfer)
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#dom-messageport-postmessage>
|
||||
fn PostMessage_(
|
||||
&self,
|
||||
cx: SafeJSContext,
|
||||
message: HandleValue,
|
||||
options: RootedTraceableBox<PostMessageOptions>,
|
||||
) -> ErrorResult {
|
||||
if self.detached.get() {
|
||||
return Ok(());
|
||||
}
|
||||
let mut rooted = CustomAutoRooter::new(
|
||||
options
|
||||
.transfer
|
||||
.as_ref()
|
||||
.unwrap_or(&Vec::with_capacity(0))
|
||||
.iter()
|
||||
.map(|js: &RootedTraceableBox<Heap<*mut JSObject>>| js.get())
|
||||
.collect(),
|
||||
);
|
||||
let guard = CustomAutoRooterGuard::new(*cx, &mut rooted);
|
||||
self.post_message_impl(cx, message, guard)
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#dom-messageport-start>
|
||||
fn Start(&self) {
|
||||
if self.detached.get() {
|
||||
return;
|
||||
}
|
||||
self.global().start_message_port(self.message_port_id());
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#dom-messageport-close>
|
||||
fn Close(&self) {
|
||||
if self.detached.get() {
|
||||
return;
|
||||
}
|
||||
self.detached.set(true);
|
||||
self.global().close_message_port(self.message_port_id());
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#handler-messageport-onmessage>
|
||||
fn GetOnmessage(&self) -> Option<Rc<EventHandlerNonNull>> {
|
||||
if self.detached.get() {
|
||||
return None;
|
||||
}
|
||||
let eventtarget = self.upcast::<EventTarget>();
|
||||
eventtarget.get_event_handler_common("message")
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#handler-messageport-onmessage>
|
||||
fn SetOnmessage(&self, listener: Option<Rc<EventHandlerNonNull>>) {
|
||||
if self.detached.get() {
|
||||
return;
|
||||
}
|
||||
self.set_onmessage(listener);
|
||||
// Note: we cannot use the event_handler macro, due to the need to start the port.
|
||||
self.global().start_message_port(self.message_port_id());
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#handler-messageport-onmessageerror>
|
||||
event_handler!(messageerror, GetOnmessageerror, SetOnmessageerror);
|
||||
}
|
|
@ -401,7 +401,9 @@ pub mod mediaquerylist;
|
|||
pub mod mediaquerylistevent;
|
||||
pub mod mediastream;
|
||||
pub mod mediastreamtrack;
|
||||
pub mod messagechannel;
|
||||
pub mod messageevent;
|
||||
pub mod messageport;
|
||||
pub mod mimetype;
|
||||
pub mod mimetypearray;
|
||||
pub mod mouseevent;
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
use crate::dom::abstractworker::SimpleWorkerErrorHandler;
|
||||
use crate::dom::bindings::cell::DomRefCell;
|
||||
use crate::dom::bindings::codegen::Bindings::MessagePortBinding::PostMessageOptions;
|
||||
use crate::dom::bindings::codegen::Bindings::ServiceWorkerBinding::{
|
||||
ServiceWorkerMethods, ServiceWorkerState, Wrap,
|
||||
};
|
||||
|
@ -13,13 +14,15 @@ 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::USVString;
|
||||
use crate::dom::bindings::structuredclone::StructuredCloneData;
|
||||
use crate::dom::bindings::structuredclone;
|
||||
use crate::dom::bindings::trace::RootedTraceableBox;
|
||||
use crate::dom::eventtarget::EventTarget;
|
||||
use crate::dom::globalscope::GlobalScope;
|
||||
use crate::script_runtime::JSContext;
|
||||
use crate::task::TaskOnce;
|
||||
use dom_struct::dom_struct;
|
||||
use js::rust::HandleValue;
|
||||
use js::jsapi::{Heap, JSObject};
|
||||
use js::rust::{CustomAutoRooter, CustomAutoRooterGuard, HandleValue};
|
||||
use script_traits::{DOMMessage, ScriptMsg};
|
||||
use servo_url::ServoUrl;
|
||||
use std::cell::Cell;
|
||||
|
@ -77,6 +80,34 @@ impl ServiceWorker {
|
|||
pub fn get_script_url(&self) -> ServoUrl {
|
||||
ServoUrl::parse(&self.script_url.borrow().clone()).unwrap()
|
||||
}
|
||||
|
||||
/// https://w3c.github.io/ServiceWorker/#service-worker-postmessage
|
||||
fn post_message_impl(
|
||||
&self,
|
||||
cx: JSContext,
|
||||
message: HandleValue,
|
||||
transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
|
||||
) -> ErrorResult {
|
||||
// Step 1
|
||||
if let ServiceWorkerState::Redundant = self.state.get() {
|
||||
return Err(Error::InvalidState);
|
||||
}
|
||||
// Step 7
|
||||
let data = structuredclone::write(cx, message, Some(transfer))?;
|
||||
let incumbent = GlobalScope::incumbent().expect("no incumbent global?");
|
||||
let msg_vec = DOMMessage {
|
||||
origin: incumbent.origin().immutable().clone(),
|
||||
data,
|
||||
};
|
||||
let _ = self
|
||||
.global()
|
||||
.script_to_constellation_chan()
|
||||
.send(ScriptMsg::ForwardDOMMessage(
|
||||
msg_vec,
|
||||
self.scope_url.clone(),
|
||||
));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl ServiceWorkerMethods for ServiceWorker {
|
||||
|
@ -90,23 +121,34 @@ impl ServiceWorkerMethods for ServiceWorker {
|
|||
USVString(self.script_url.borrow().clone())
|
||||
}
|
||||
|
||||
// https://w3c.github.io/ServiceWorker/#service-worker-postmessage
|
||||
fn PostMessage(&self, cx: JSContext, message: HandleValue) -> ErrorResult {
|
||||
// Step 1
|
||||
if let ServiceWorkerState::Redundant = self.state.get() {
|
||||
return Err(Error::InvalidState);
|
||||
}
|
||||
// Step 7
|
||||
let data = StructuredCloneData::write(*cx, message)?;
|
||||
let msg_vec = DOMMessage(data.move_to_arraybuffer());
|
||||
let _ = self
|
||||
.global()
|
||||
.script_to_constellation_chan()
|
||||
.send(ScriptMsg::ForwardDOMMessage(
|
||||
msg_vec,
|
||||
self.scope_url.clone(),
|
||||
));
|
||||
Ok(())
|
||||
/// https://w3c.github.io/ServiceWorker/#service-worker-postmessage
|
||||
fn PostMessage(
|
||||
&self,
|
||||
cx: JSContext,
|
||||
message: HandleValue,
|
||||
transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
|
||||
) -> ErrorResult {
|
||||
self.post_message_impl(cx, message, transfer)
|
||||
}
|
||||
|
||||
/// https://w3c.github.io/ServiceWorker/#service-worker-postmessage
|
||||
fn PostMessage_(
|
||||
&self,
|
||||
cx: JSContext,
|
||||
message: HandleValue,
|
||||
options: RootedTraceableBox<PostMessageOptions>,
|
||||
) -> ErrorResult {
|
||||
let mut rooted = CustomAutoRooter::new(
|
||||
options
|
||||
.transfer
|
||||
.as_ref()
|
||||
.unwrap_or(&Vec::with_capacity(0))
|
||||
.iter()
|
||||
.map(|js: &RootedTraceableBox<Heap<*mut JSObject>>| js.get())
|
||||
.collect(),
|
||||
);
|
||||
let guard = CustomAutoRooterGuard::new(*cx, &mut rooted);
|
||||
self.post_message_impl(cx, message, guard)
|
||||
}
|
||||
|
||||
// https://w3c.github.io/ServiceWorker/#service-worker-container-onerror-attribute
|
||||
|
|
|
@ -12,12 +12,14 @@ use crate::dom::bindings::codegen::Bindings::WorkerBinding::WorkerType;
|
|||
use crate::dom::bindings::inheritance::Castable;
|
||||
use crate::dom::bindings::root::{DomRoot, RootCollection, ThreadLocalStackRoots};
|
||||
use crate::dom::bindings::str::DOMString;
|
||||
use crate::dom::bindings::structuredclone;
|
||||
use crate::dom::dedicatedworkerglobalscope::AutoWorkerReset;
|
||||
use crate::dom::event::Event;
|
||||
use crate::dom::eventtarget::EventTarget;
|
||||
use crate::dom::extendableevent::ExtendableEvent;
|
||||
use crate::dom::extendablemessageevent::ExtendableMessageEvent;
|
||||
use crate::dom::globalscope::GlobalScope;
|
||||
use crate::dom::messageevent::MessageEvent;
|
||||
use crate::dom::worker::TrustedWorkerAddress;
|
||||
use crate::dom::workerglobalscope::WorkerGlobalScope;
|
||||
use crate::fetch::load_whole_resource;
|
||||
|
@ -409,13 +411,22 @@ impl ServiceWorkerGlobalScope {
|
|||
use self::ServiceWorkerScriptMsg::*;
|
||||
|
||||
match msg {
|
||||
CommonWorker(WorkerScriptMsg::DOMMessage(data)) => {
|
||||
CommonWorker(WorkerScriptMsg::DOMMessage { data, .. }) => {
|
||||
let scope = self.upcast::<WorkerGlobalScope>();
|
||||
let target = self.upcast();
|
||||
let _ac = enter_realm(&*scope);
|
||||
rooted!(in(*scope.get_cx()) let mut message = UndefinedValue());
|
||||
data.read(scope.upcast(), message.handle_mut());
|
||||
ExtendableMessageEvent::dispatch_jsval(target, scope.upcast(), message.handle());
|
||||
if let Ok(ports) = structuredclone::read(scope.upcast(), data, message.handle_mut())
|
||||
{
|
||||
ExtendableMessageEvent::dispatch_jsval(
|
||||
target,
|
||||
scope.upcast(),
|
||||
message.handle(),
|
||||
ports,
|
||||
);
|
||||
} else {
|
||||
MessageEvent::dispatch_error(target, scope.upcast());
|
||||
}
|
||||
},
|
||||
CommonWorker(WorkerScriptMsg::Common(msg)) => {
|
||||
self.upcast::<WorkerGlobalScope>().process_event(msg);
|
||||
|
|
|
@ -5,9 +5,9 @@
|
|||
// https://html.spec.whatwg.org/multipage/#dedicatedworkerglobalscope
|
||||
[Global=(Worker,DedicatedWorker), Exposed=DedicatedWorker]
|
||||
/*sealed*/ interface DedicatedWorkerGlobalScope : WorkerGlobalScope {
|
||||
[Throws]
|
||||
void postMessage(any message/*, optional sequence<Transferable> transfer*/);
|
||||
attribute EventHandler onmessage;
|
||||
[Throws] void postMessage(any message, sequence<object> transfer);
|
||||
[Throws] void postMessage(any message, optional PostMessageOptions options = {});
|
||||
attribute EventHandler onmessage;
|
||||
|
||||
void close();
|
||||
};
|
||||
|
|
|
@ -25,7 +25,8 @@ interface DissimilarOriginWindow : GlobalScope {
|
|||
|
||||
void close();
|
||||
readonly attribute boolean closed;
|
||||
[Throws] void postMessage(any message, DOMString targetOrigin);
|
||||
[Throws] void postMessage(any message, USVString targetOrigin, optional sequence<object> transfer /*= []*/);
|
||||
[Throws] void postMessage(any message, optional WindowPostMessageOptions options = {});
|
||||
attribute any opener;
|
||||
void blur();
|
||||
void focus();
|
||||
|
|
|
@ -12,7 +12,7 @@ interface ExtendableMessageEvent : ExtendableEvent {
|
|||
readonly attribute DOMString origin;
|
||||
readonly attribute DOMString lastEventId;
|
||||
// [SameObject] readonly attribute (Client or ServiceWorker /*or MessagePort*/)? source;
|
||||
// readonly attribute FrozenArray<MessagePort>? ports;
|
||||
readonly attribute /*FrozenArray<MessagePort>*/any ports;
|
||||
};
|
||||
|
||||
dictionary ExtendableMessageEventInit : ExtendableEventInit {
|
||||
|
|
14
components/script/dom/webidls/MessageChannel.webidl
Normal file
14
components/script/dom/webidls/MessageChannel.webidl
Normal file
|
@ -0,0 +1,14 @@
|
|||
/* 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/. */
|
||||
/*
|
||||
* The origin of this IDL file is:
|
||||
* https://html.spec.whatwg.org/multipage/#messagechannel
|
||||
*/
|
||||
|
||||
[Exposed=(Window,Worker)]
|
||||
interface MessageChannel {
|
||||
[Throws] constructor();
|
||||
readonly attribute MessagePort port1;
|
||||
readonly attribute MessagePort port2;
|
||||
};
|
|
@ -12,7 +12,7 @@ interface MessageEvent : Event {
|
|||
// FIXME(#22617): WindowProxy is not exposed in Worker globals
|
||||
readonly attribute object? source;
|
||||
//readonly attribute (WindowProxy or MessagePort)? source;
|
||||
//readonly attribute MessagePort[]? ports;
|
||||
readonly attribute /*FrozenArray<MessagePort>*/any ports;
|
||||
};
|
||||
|
||||
dictionary MessageEventInit : EventInit {
|
||||
|
@ -22,5 +22,7 @@ dictionary MessageEventInit : EventInit {
|
|||
//DOMString channel;
|
||||
Window? source;
|
||||
//(WindowProxy or MessagePort)? source;
|
||||
//sequence<MessagePort> ports;
|
||||
sequence<MessagePort> ports;
|
||||
};
|
||||
|
||||
typedef (/*WindowProxy or */MessagePort or ServiceWorker) MessageEventSource;
|
||||
|
|
23
components/script/dom/webidls/MessagePort.webidl
Normal file
23
components/script/dom/webidls/MessagePort.webidl
Normal file
|
@ -0,0 +1,23 @@
|
|||
/* 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/. */
|
||||
/*
|
||||
* The origin of this IDL file is:
|
||||
* https://html.spec.whatwg.org/multipage/#messageport
|
||||
*/
|
||||
|
||||
[Exposed=(Window,Worker)]
|
||||
interface MessagePort : EventTarget {
|
||||
[Throws] void postMessage(any message, sequence<object> transfer /*= []*/);
|
||||
[Throws] void postMessage(any message, optional PostMessageOptions options = {});
|
||||
void start();
|
||||
void close();
|
||||
|
||||
// event handlers
|
||||
attribute EventHandler onmessage;
|
||||
attribute EventHandler onmessageerror;
|
||||
};
|
||||
|
||||
dictionary PostMessageOptions {
|
||||
sequence<object> transfer;
|
||||
};
|
|
@ -7,7 +7,8 @@
|
|||
interface ServiceWorker : EventTarget {
|
||||
readonly attribute USVString scriptURL;
|
||||
readonly attribute ServiceWorkerState state;
|
||||
[Throws] void postMessage(any message/*, optional sequence<object> transfer = []*/);
|
||||
[Throws] void postMessage(any message, sequence<object> transfer);
|
||||
[Throws] void postMessage(any message, optional PostMessageOptions options = {});
|
||||
|
||||
// event
|
||||
attribute EventHandler onstatechange;
|
||||
|
|
|
@ -63,9 +63,10 @@
|
|||
unsigned long requestAnimationFrame(FrameRequestCallback callback);
|
||||
void cancelAnimationFrame(unsigned long handle);
|
||||
|
||||
//void postMessage(any message, DOMString targetOrigin, optional sequence<Transferable> transfer);
|
||||
[Throws]
|
||||
void postMessage(any message, DOMString targetOrigin);
|
||||
void postMessage(any message, USVString targetOrigin, optional sequence<object> transfer /*= []*/);
|
||||
[Throws]
|
||||
void postMessage(any message, optional WindowPostMessageOptions options = {});
|
||||
|
||||
// also has obsolete members
|
||||
};
|
||||
|
@ -173,3 +174,8 @@ partial interface Window {
|
|||
[Pref="css.animations.testing.enabled"]
|
||||
readonly attribute unsigned long runningAnimationCount;
|
||||
};
|
||||
|
||||
dictionary WindowPostMessageOptions {
|
||||
USVString targetOrigin = "/";
|
||||
sequence<object> transfer;
|
||||
};
|
||||
|
|
|
@ -14,8 +14,8 @@ interface Worker : EventTarget {
|
|||
[Throws] constructor(USVString scriptURL, optional WorkerOptions options = {});
|
||||
void terminate();
|
||||
|
||||
[Throws] void postMessage(any message/*, sequence<object> transfer*/);
|
||||
// void postMessage(any message, optional PostMessageOptions options);
|
||||
[Throws] void postMessage(any message, sequence<object> transfer);
|
||||
[Throws] void postMessage(any message, optional PostMessageOptions options = {});
|
||||
attribute EventHandler onmessage;
|
||||
attribute EventHandler onmessageerror;
|
||||
};
|
||||
|
|
|
@ -598,6 +598,7 @@ impl TaskOnce for MessageReceivedTask {
|
|||
message.handle(),
|
||||
Some(&ws.origin().ascii_serialization()),
|
||||
None,
|
||||
vec![],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ use crate::dom::bindings::codegen::Bindings::MediaQueryListBinding::MediaQueryLi
|
|||
use crate::dom::bindings::codegen::Bindings::PermissionStatusBinding::PermissionState;
|
||||
use crate::dom::bindings::codegen::Bindings::RequestBinding::RequestInit;
|
||||
use crate::dom::bindings::codegen::Bindings::WindowBinding::{
|
||||
self, FrameRequestCallback, WindowMethods,
|
||||
self, FrameRequestCallback, WindowMethods, WindowPostMessageOptions,
|
||||
};
|
||||
use crate::dom::bindings::codegen::Bindings::WindowBinding::{ScrollBehavior, ScrollToOptions};
|
||||
use crate::dom::bindings::codegen::UnionTypes::RequestOrUSVString;
|
||||
|
@ -24,7 +24,7 @@ use crate::dom::bindings::refcounted::Trusted;
|
|||
use crate::dom::bindings::reflector::DomObject;
|
||||
use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
|
||||
use crate::dom::bindings::str::{DOMString, USVString};
|
||||
use crate::dom::bindings::structuredclone::StructuredCloneData;
|
||||
use crate::dom::bindings::structuredclone;
|
||||
use crate::dom::bindings::trace::RootedTraceableBox;
|
||||
use crate::dom::bindings::utils::{GlobalStaticData, WindowProxyHandler};
|
||||
use crate::dom::bindings::weakref::DOMTracker;
|
||||
|
@ -79,13 +79,15 @@ use euclid::default::{Point2D as UntypedPoint2D, Rect as UntypedRect};
|
|||
use euclid::{Point2D, Rect, Scale, Size2D, Vector2D};
|
||||
use ipc_channel::ipc::{channel, IpcSender};
|
||||
use ipc_channel::router::ROUTER;
|
||||
use js::jsapi::Heap;
|
||||
use js::jsapi::JSAutoRealm;
|
||||
use js::jsapi::JSObject;
|
||||
use js::jsapi::JSPROP_ENUMERATE;
|
||||
use js::jsapi::{GCReason, JS_GC};
|
||||
use js::jsval::UndefinedValue;
|
||||
use js::jsval::{JSVal, NullValue};
|
||||
use js::rust::wrappers::JS_DefineProperty;
|
||||
use js::rust::HandleValue;
|
||||
use js::rust::{CustomAutoRooter, CustomAutoRooterGuard, HandleValue};
|
||||
use media::WindowGLContext;
|
||||
use msg::constellation_msg::PipelineId;
|
||||
use net_traits::image_cache::{ImageCache, ImageResponder, ImageResponse};
|
||||
|
@ -104,7 +106,10 @@ use script_layout_interface::rpc::{
|
|||
use script_layout_interface::{PendingImageState, TrustedNodeAddress};
|
||||
use script_traits::webdriver_msg::{WebDriverJSError, WebDriverJSResult};
|
||||
use script_traits::{ConstellationControlMsg, DocumentState, HistoryEntryReplacement, LoadData};
|
||||
use script_traits::{ScriptMsg, ScriptToConstellationChan, ScrollState, TimerEvent, TimerEventId};
|
||||
use script_traits::{
|
||||
ScriptMsg, ScriptToConstellationChan, ScrollState, StructuredSerializedData, TimerEvent,
|
||||
TimerEventId,
|
||||
};
|
||||
use script_traits::{TimerSchedulerMsg, WindowSizeData, WindowSizeType};
|
||||
use selectors::attr::CaseSensitivity;
|
||||
use servo_geometry::{f32_rect_to_au_rect, MaxRect};
|
||||
|
@ -969,27 +974,57 @@ impl WindowMethods for Window {
|
|||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/#dom-window-postmessage
|
||||
fn PostMessage(&self, cx: JSContext, message: HandleValue, origin: DOMString) -> ErrorResult {
|
||||
let source_global = GlobalScope::incumbent().expect("no incumbent global??");
|
||||
let source = source_global.as_window();
|
||||
fn PostMessage(
|
||||
&self,
|
||||
cx: JSContext,
|
||||
message: HandleValue,
|
||||
target_origin: USVString,
|
||||
mut transfer: CustomAutoRooterGuard<Option<Vec<*mut JSObject>>>,
|
||||
) -> ErrorResult {
|
||||
let incumbent = GlobalScope::incumbent().expect("no incumbent global?");
|
||||
let source = incumbent.as_window();
|
||||
let source_origin = source.Document().origin().immutable().clone();
|
||||
|
||||
// Step 3-5.
|
||||
let origin = match &origin[..] {
|
||||
"*" => None,
|
||||
"/" => Some(source.Document().origin().immutable().clone()),
|
||||
url => match ServoUrl::parse(&url) {
|
||||
Ok(url) => Some(url.origin().clone()),
|
||||
Err(_) => return Err(Error::Syntax),
|
||||
},
|
||||
};
|
||||
if transfer.is_some() {
|
||||
let mut rooted = CustomAutoRooter::new(transfer.take().unwrap());
|
||||
let transfer = Some(CustomAutoRooterGuard::new(*cx, &mut rooted));
|
||||
self.post_message_impl(&target_origin, source_origin, source, cx, message, transfer)
|
||||
} else {
|
||||
self.post_message_impl(&target_origin, source_origin, source, cx, message, None)
|
||||
}
|
||||
}
|
||||
|
||||
// Step 1-2, 6-8.
|
||||
// TODO(#12717): Should implement the `transfer` argument.
|
||||
let data = StructuredCloneData::write(*cx, message)?;
|
||||
/// <https://html.spec.whatwg.org/multipage/#dom-messageport-postmessage>
|
||||
fn PostMessage_(
|
||||
&self,
|
||||
cx: JSContext,
|
||||
message: HandleValue,
|
||||
options: RootedTraceableBox<WindowPostMessageOptions>,
|
||||
) -> ErrorResult {
|
||||
let mut rooted = CustomAutoRooter::new(
|
||||
options
|
||||
.transfer
|
||||
.as_ref()
|
||||
.unwrap_or(&Vec::with_capacity(0))
|
||||
.iter()
|
||||
.map(|js: &RootedTraceableBox<Heap<*mut JSObject>>| js.get())
|
||||
.collect(),
|
||||
);
|
||||
let transfer = Some(CustomAutoRooterGuard::new(*cx, &mut rooted));
|
||||
|
||||
// Step 9.
|
||||
self.post_message(origin, &*source.window_proxy(), data);
|
||||
Ok(())
|
||||
let incumbent = GlobalScope::incumbent().expect("no incumbent global?");
|
||||
let source = incumbent.as_window();
|
||||
|
||||
let source_origin = source.Document().origin().immutable().clone();
|
||||
|
||||
self.post_message_impl(
|
||||
&options.targetOrigin,
|
||||
source_origin,
|
||||
source,
|
||||
cx,
|
||||
message,
|
||||
transfer,
|
||||
)
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/#dom-window-captureevents
|
||||
|
@ -1287,6 +1322,34 @@ impl WindowMethods for Window {
|
|||
}
|
||||
|
||||
impl Window {
|
||||
/// https://html.spec.whatwg.org/multipage/#window-post-message-steps
|
||||
fn post_message_impl(
|
||||
&self,
|
||||
target_origin: &USVString,
|
||||
source_origin: ImmutableOrigin,
|
||||
source: &Window,
|
||||
cx: JSContext,
|
||||
message: HandleValue,
|
||||
transfer: Option<CustomAutoRooterGuard<Vec<*mut JSObject>>>,
|
||||
) -> ErrorResult {
|
||||
// Step 1-2, 6-8.
|
||||
let data = structuredclone::write(cx, message, transfer)?;
|
||||
|
||||
// Step 3-5.
|
||||
let target_origin = match target_origin.0[..].as_ref() {
|
||||
"*" => None,
|
||||
"/" => Some(source_origin.clone()),
|
||||
url => match ServoUrl::parse(&url) {
|
||||
Ok(url) => Some(url.origin().clone()),
|
||||
Err(_) => return Err(Error::Syntax),
|
||||
},
|
||||
};
|
||||
|
||||
// Step 9.
|
||||
self.post_message(target_origin, source_origin, &*source.window_proxy(), data);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// https://drafts.css-houdini.org/css-paint-api-1/#paint-worklet
|
||||
pub fn paint_worklet(&self) -> DomRoot<Worklet> {
|
||||
self.paint_worklet.or_init(|| self.new_paint_worklet())
|
||||
|
@ -1333,6 +1396,9 @@ impl Window {
|
|||
// thread, informing it that it can safely free the memory.
|
||||
self.Document().upcast::<Node>().teardown();
|
||||
|
||||
// Tell the constellation to drop the sender to our message-port router, if there is any.
|
||||
self.upcast::<GlobalScope>().remove_message_ports_router();
|
||||
|
||||
// Clean up any active promises
|
||||
// https://github.com/servo/servo/issues/15318
|
||||
if let Some(custom_elements) = self.custom_element_registry.get() {
|
||||
|
@ -2334,18 +2400,20 @@ impl Window {
|
|||
pub fn post_message(
|
||||
&self,
|
||||
target_origin: Option<ImmutableOrigin>,
|
||||
source_origin: ImmutableOrigin,
|
||||
source: &WindowProxy,
|
||||
serialize_with_transfer_result: StructuredCloneData,
|
||||
data: StructuredSerializedData,
|
||||
) {
|
||||
let this = Trusted::new(self);
|
||||
let source = Trusted::new(source);
|
||||
let task = task!(post_serialised_message: move || {
|
||||
let this = this.root();
|
||||
let source = source.root();
|
||||
let document = this.Document();
|
||||
|
||||
// Step 7.1.
|
||||
if let Some(target_origin) = target_origin {
|
||||
if !target_origin.same_origin(this.Document().origin()) {
|
||||
if let Some(ref target_origin) = target_origin {
|
||||
if !target_origin.same_origin(document.origin()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -2355,23 +2423,23 @@ impl Window {
|
|||
let obj = this.reflector().get_jsobject();
|
||||
let _ac = JSAutoRealm::new(*cx, obj.get());
|
||||
rooted!(in(*cx) let mut message_clone = UndefinedValue());
|
||||
serialize_with_transfer_result.read(
|
||||
this.upcast(),
|
||||
message_clone.handle_mut(),
|
||||
);
|
||||
|
||||
// Step 7.6.
|
||||
// TODO: MessagePort array.
|
||||
|
||||
// Step 7.7.
|
||||
// TODO(#12719): Set the other attributes.
|
||||
MessageEvent::dispatch_jsval(
|
||||
this.upcast(),
|
||||
this.upcast(),
|
||||
message_clone.handle(),
|
||||
None,
|
||||
Some(&*source),
|
||||
);
|
||||
if let Ok(ports) = structuredclone::read(this.upcast(), data, message_clone.handle_mut()) {
|
||||
// Step 7.6, 7.7
|
||||
MessageEvent::dispatch_jsval(
|
||||
this.upcast(),
|
||||
this.upcast(),
|
||||
message_clone.handle(),
|
||||
Some(&source_origin.ascii_serialization()),
|
||||
Some(&*source),
|
||||
ports,
|
||||
);
|
||||
} else {
|
||||
// Step 4, fire messageerror.
|
||||
MessageEvent::dispatch_error(
|
||||
this.upcast(),
|
||||
this.upcast(),
|
||||
);
|
||||
}
|
||||
});
|
||||
// FIXME(nox): Why are errors silenced here?
|
||||
// TODO(#12718): Use the "posted message task source".
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
use crate::compartments::enter_realm;
|
||||
use crate::dom::abstractworker::SimpleWorkerErrorHandler;
|
||||
use crate::dom::abstractworker::WorkerScriptMsg;
|
||||
use crate::dom::bindings::codegen::Bindings::MessagePortBinding::PostMessageOptions;
|
||||
use crate::dom::bindings::codegen::Bindings::WorkerBinding;
|
||||
use crate::dom::bindings::codegen::Bindings::WorkerBinding::{WorkerMethods, WorkerOptions};
|
||||
use crate::dom::bindings::error::{Error, ErrorResult, Fallible};
|
||||
|
@ -13,7 +14,8 @@ 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::USVString;
|
||||
use crate::dom::bindings::structuredclone::StructuredCloneData;
|
||||
use crate::dom::bindings::structuredclone;
|
||||
use crate::dom::bindings::trace::RootedTraceableBox;
|
||||
use crate::dom::dedicatedworkerglobalscope::{
|
||||
DedicatedWorkerGlobalScope, DedicatedWorkerScriptMsg,
|
||||
};
|
||||
|
@ -27,10 +29,10 @@ use crossbeam_channel::{unbounded, Sender};
|
|||
use devtools_traits::{DevtoolsPageInfo, ScriptToDevtoolsControlMsg};
|
||||
use dom_struct::dom_struct;
|
||||
use ipc_channel::ipc;
|
||||
use js::jsapi::JS_RequestInterruptCallback;
|
||||
use js::jsapi::{Heap, JSObject, JS_RequestInterruptCallback};
|
||||
use js::jsval::UndefinedValue;
|
||||
use js::rust::HandleValue;
|
||||
use script_traits::WorkerScriptLoadOrigin;
|
||||
use js::rust::{CustomAutoRooter, CustomAutoRooterGuard, HandleValue};
|
||||
use script_traits::{StructuredSerializedData, WorkerScriptLoadOrigin};
|
||||
use std::cell::Cell;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::Arc;
|
||||
|
@ -137,7 +139,11 @@ impl Worker {
|
|||
self.terminated.get()
|
||||
}
|
||||
|
||||
pub fn handle_message(address: TrustedWorkerAddress, data: StructuredCloneData) {
|
||||
pub fn handle_message(
|
||||
address: TrustedWorkerAddress,
|
||||
origin: String,
|
||||
data: StructuredSerializedData,
|
||||
) {
|
||||
let worker = address.root();
|
||||
|
||||
if worker.is_terminated() {
|
||||
|
@ -148,30 +154,79 @@ impl Worker {
|
|||
let target = worker.upcast();
|
||||
let _ac = enter_realm(target);
|
||||
rooted!(in(*global.get_cx()) let mut message = UndefinedValue());
|
||||
data.read(&global, message.handle_mut());
|
||||
MessageEvent::dispatch_jsval(target, &global, message.handle(), None, None);
|
||||
if let Ok(ports) = structuredclone::read(&global, data, message.handle_mut()) {
|
||||
MessageEvent::dispatch_jsval(
|
||||
target,
|
||||
&global,
|
||||
message.handle(),
|
||||
Some(&origin),
|
||||
None,
|
||||
ports,
|
||||
);
|
||||
} else {
|
||||
// Step 4 of the "port post message steps" of the implicit messageport, fire messageerror.
|
||||
MessageEvent::dispatch_error(target, &global);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dispatch_simple_error(address: TrustedWorkerAddress) {
|
||||
let worker = address.root();
|
||||
worker.upcast().fire_event(atom!("error"));
|
||||
}
|
||||
}
|
||||
|
||||
impl WorkerMethods for Worker {
|
||||
// https://html.spec.whatwg.org/multipage/#dom-worker-postmessage
|
||||
fn PostMessage(&self, cx: JSContext, message: HandleValue) -> ErrorResult {
|
||||
let data = StructuredCloneData::write(*cx, message)?;
|
||||
/// https://html.spec.whatwg.org/multipage/#dom-dedicatedworkerglobalscope-postmessage
|
||||
fn post_message_impl(
|
||||
&self,
|
||||
cx: JSContext,
|
||||
message: HandleValue,
|
||||
transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
|
||||
) -> ErrorResult {
|
||||
let data = structuredclone::write(cx, message, Some(transfer))?;
|
||||
let address = Trusted::new(self);
|
||||
|
||||
// NOTE: step 9 of https://html.spec.whatwg.org/multipage/#dom-messageport-postmessage
|
||||
// indicates that a nonexistent communication channel should result in a silent error.
|
||||
let _ = self.sender.send(DedicatedWorkerScriptMsg::CommonWorker(
|
||||
address,
|
||||
WorkerScriptMsg::DOMMessage(data),
|
||||
WorkerScriptMsg::DOMMessage {
|
||||
origin: self.global().origin().immutable().clone(),
|
||||
data,
|
||||
},
|
||||
));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl WorkerMethods for Worker {
|
||||
/// https://html.spec.whatwg.org/multipage/#dom-worker-postmessage
|
||||
fn PostMessage(
|
||||
&self,
|
||||
cx: JSContext,
|
||||
message: HandleValue,
|
||||
transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
|
||||
) -> ErrorResult {
|
||||
self.post_message_impl(cx, message, transfer)
|
||||
}
|
||||
|
||||
/// https://html.spec.whatwg.org/multipage/#dom-worker-postmessage
|
||||
fn PostMessage_(
|
||||
&self,
|
||||
cx: JSContext,
|
||||
message: HandleValue,
|
||||
options: RootedTraceableBox<PostMessageOptions>,
|
||||
) -> ErrorResult {
|
||||
let mut rooted = CustomAutoRooter::new(
|
||||
options
|
||||
.transfer
|
||||
.as_ref()
|
||||
.unwrap_or(&Vec::with_capacity(0))
|
||||
.iter()
|
||||
.map(|js: &RootedTraceableBox<Heap<*mut JSObject>>| js.get())
|
||||
.collect(),
|
||||
);
|
||||
let guard = CustomAutoRooterGuard::new(*cx, &mut rooted);
|
||||
self.post_message_impl(cx, message, guard)
|
||||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
// https://html.spec.whatwg.org/multipage/#terminate-a-worker
|
||||
|
|
|
@ -33,6 +33,7 @@ use crate::task_source::dom_manipulation::DOMManipulationTaskSource;
|
|||
use crate::task_source::file_reading::FileReadingTaskSource;
|
||||
use crate::task_source::networking::NetworkingTaskSource;
|
||||
use crate::task_source::performance_timeline::PerformanceTimelineTaskSource;
|
||||
use crate::task_source::port_message::PortMessageQueue;
|
||||
use crate::task_source::remote_event::RemoteEventTaskSource;
|
||||
use crate::task_source::websocket::WebsocketTaskSource;
|
||||
use crate::timers::{IsInterval, TimerCallback};
|
||||
|
@ -457,6 +458,10 @@ impl WorkerGlobalScope {
|
|||
PerformanceTimelineTaskSource(self.script_chan(), self.pipeline_id())
|
||||
}
|
||||
|
||||
pub fn port_message_queue(&self) -> PortMessageQueue {
|
||||
PortMessageQueue(self.script_chan(), self.pipeline_id())
|
||||
}
|
||||
|
||||
pub fn remote_event_task_source(&self) -> RemoteEventTaskSource {
|
||||
RemoteEventTaskSource(self.script_chan(), self.pipeline_id())
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue