mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
begin messageport, transferable objects, impl
Accept transfer argument for StructuredCloneData::write Allow structured clone reads to return a boolean Add Transferable trait Add basic skeletons to MessagePort Implement transfer and transfer-receiving steps on MessagePort Use transfer and transfer_receive in StructuredClone callbacks Implement MessageChannel Freeze the array object for the MessageEvent ports attribute Implement transfer argument on window.postMessage Use ReentrantMutex instead for MessagePortInternal Accept origin as a parameter in dispatch_jsval Fix BorrowMut crash with pending_port_message Detach port on closure and check for detached during transfer Enable webmessaging tests fix webidl fix
This commit is contained in:
parent
605ddbecd4
commit
c3b17c1201
37 changed files with 801 additions and 81 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -3154,6 +3154,7 @@ dependencies = [
|
|||
"malloc_size_of_derive",
|
||||
"parking_lot",
|
||||
"serde",
|
||||
"servo_url",
|
||||
"size_of_test",
|
||||
"webrender_api",
|
||||
]
|
||||
|
@ -4134,6 +4135,7 @@ dependencies = [
|
|||
"app_units",
|
||||
"backtrace",
|
||||
"base64",
|
||||
"bincode",
|
||||
"bitflags",
|
||||
"bluetooth_traits",
|
||||
"canvas_traits",
|
||||
|
|
|
@ -419,6 +419,7 @@ pub enum ScriptHangAnnotation {
|
|||
ExitFullscreen,
|
||||
WebVREvent,
|
||||
PerformanceTimelineTask,
|
||||
PortMessage,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
|
||||
|
|
|
@ -136,6 +136,7 @@ impl Formattable for ProfilerCategory {
|
|||
ProfilerCategory::ScriptParseHTML => "Script Parse HTML",
|
||||
ProfilerCategory::ScriptParseXML => "Script Parse XML",
|
||||
ProfilerCategory::ScriptPlannedNavigation => "Script Planned Navigation",
|
||||
ProfilerCategory::ScriptPortMessage => "Script Port Message",
|
||||
ProfilerCategory::ScriptResize => "Script Resize",
|
||||
ProfilerCategory::ScriptEvent => "Script Event",
|
||||
ProfilerCategory::ScriptUpdateReplacedElement => "Script Update Replaced Element",
|
||||
|
|
|
@ -108,6 +108,7 @@ pub enum ProfilerCategory {
|
|||
ScriptWorkletEvent = 0x7a,
|
||||
ScriptPerformanceEvent = 0x7b,
|
||||
ScriptHistoryEvent = 0x7c,
|
||||
ScriptPortMessage = 0x7d,
|
||||
TimeToFirstPaint = 0x80,
|
||||
TimeToFirstContentfulPaint = 0x81,
|
||||
TimeToInteractive = 0x82,
|
||||
|
|
|
@ -97,6 +97,7 @@ servo_config = {path = "../config"}
|
|||
servo_geometry = {path = "../geometry" }
|
||||
servo-media = {git = "https://github.com/servo/media"}
|
||||
servo_rand = {path = "../rand"}
|
||||
servo_remutex = {path = "../remutex"}
|
||||
servo_url = {path = "../url"}
|
||||
sparkle = "0.1"
|
||||
smallvec = { version = "0.6", features = ["std", "union"] }
|
||||
|
|
|
@ -12,7 +12,10 @@ pub enum WorkerScriptMsg {
|
|||
/// Common variants associated with the script messages
|
||||
Common(CommonScriptMsg),
|
||||
/// Message sent through Worker.postMessage
|
||||
DOMMessage(StructuredCloneData),
|
||||
DOMMessage {
|
||||
origin: String,
|
||||
data: StructuredCloneData,
|
||||
}
|
||||
}
|
||||
|
||||
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!"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -10,8 +10,10 @@ use crate::dom::bindings::conversions::root_from_handleobject;
|
|||
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 js::glue::CopyJSStructuredCloneData;
|
||||
use js::glue::DeleteJSAutoStructuredCloneBuffer;
|
||||
use js::glue::GetLengthOfJSStructuredCloneData;
|
||||
|
@ -44,6 +46,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,
|
||||
}
|
||||
|
||||
|
@ -189,26 +192,43 @@ unsafe extern "C" fn write_callback(
|
|||
}
|
||||
|
||||
unsafe extern "C" fn read_transfer_callback(
|
||||
_cx: *mut JSContext,
|
||||
_r: *mut JSStructuredCloneReader,
|
||||
_tag: u32,
|
||||
_content: *mut raw::c_void,
|
||||
_extra_data: u64,
|
||||
_closure: *mut raw::c_void,
|
||||
_return_object: RawMutableHandleObject,
|
||||
cx: *mut JSContext,
|
||||
r: *mut JSStructuredCloneReader,
|
||||
tag: u32,
|
||||
content: *mut raw::c_void,
|
||||
extra_data: u64,
|
||||
closure: *mut raw::c_void,
|
||||
return_object: RawMutableHandleObject,
|
||||
) -> bool {
|
||||
false
|
||||
if tag == StructuredCloneTags::MessagePort as u32 {
|
||||
<MessagePort as Transferable>::transfer_receive(cx, r, closure, content, extra_data, return_object)
|
||||
} else {
|
||||
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,
|
||||
_content: *mut *mut raw::c_void,
|
||||
_extra_data: *mut u64,
|
||||
obj: RawHandleObject,
|
||||
closure: *mut raw::c_void,
|
||||
tag: *mut u32,
|
||||
ownership: *mut TransferableOwnership,
|
||||
content: *mut *mut raw::c_void,
|
||||
extra_data: *mut u64,
|
||||
) -> bool {
|
||||
if let Ok(port) = root_from_handleobject::<MessagePort>(Handle::from_raw(obj)) {
|
||||
if let Some(true) = port.detached() {
|
||||
return false;
|
||||
}
|
||||
|
||||
*tag = StructuredCloneTags::MessagePort as u32;
|
||||
*ownership = TransferableOwnership::SCTAG_TMO_CUSTOM;
|
||||
if port.transfer(closure, content, extra_data) {
|
||||
port.set_detached(true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
|
@ -256,7 +276,11 @@ pub enum StructuredCloneData {
|
|||
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> {
|
||||
pub fn write(
|
||||
cx: *mut JSContext,
|
||||
message: HandleValue,
|
||||
transfer: HandleValue,
|
||||
) -> Fallible<StructuredCloneData> {
|
||||
unsafe {
|
||||
let scbuf = NewJSAutoStructuredCloneBuffer(
|
||||
StructuredCloneScope::DifferentProcess,
|
||||
|
@ -275,7 +299,7 @@ impl StructuredCloneData {
|
|||
policy,
|
||||
&STRUCTURED_CLONE_CALLBACKS,
|
||||
ptr::null_mut(),
|
||||
HandleValue::undefined(),
|
||||
transfer,
|
||||
);
|
||||
if !result {
|
||||
JS_ClearPendingException(cx);
|
||||
|
@ -306,7 +330,12 @@ impl StructuredCloneData {
|
|||
/// Reads a structured clone.
|
||||
///
|
||||
/// Panics if `JS_ReadStructuredClone` fails.
|
||||
fn read_clone(global: &GlobalScope, data: *mut u64, nbytes: size_t, rval: MutableHandleValue) {
|
||||
fn read_clone(
|
||||
global: &GlobalScope,
|
||||
data: *mut u64,
|
||||
nbytes: size_t,
|
||||
rval: MutableHandleValue,
|
||||
) -> bool {
|
||||
let cx = global.get_cx();
|
||||
let _ac = enter_realm(&*global);
|
||||
let mut sc_holder = StructuredCloneHolder { blob: None };
|
||||
|
@ -320,31 +349,33 @@ impl StructuredCloneData {
|
|||
|
||||
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
|
||||
));
|
||||
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);
|
||||
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
/// Thunk for the actual `read_clone` method. Resolves proper variant for read_clone.
|
||||
pub fn read(self, global: &GlobalScope, rval: MutableHandleValue) {
|
||||
pub fn read(self, global: &GlobalScope, rval: MutableHandleValue) -> bool {
|
||||
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::read_clone(global, data, nbytes, rval)
|
||||
}
|
||||
StructuredCloneData::Struct(data, nbytes) => {
|
||||
StructuredCloneData::read_clone(global, data, nbytes, rval)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
29
components/script/dom/bindings/transferable.rs
Normal file
29
components/script/dom/bindings/transferable.rs
Normal file
|
@ -0,0 +1,29 @@
|
|||
/* 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 js::jsapi::{JSContext, JSStructuredCloneReader, MutableHandleObject};
|
||||
use std::os::raw;
|
||||
|
||||
pub trait Transferable : DomObject {
|
||||
fn transfer(
|
||||
&self,
|
||||
closure: *mut raw::c_void,
|
||||
content: *mut *mut raw::c_void,
|
||||
extra_data: *mut u64,
|
||||
) -> bool;
|
||||
fn transfer_receive(
|
||||
cx: *mut JSContext,
|
||||
r: *mut JSStructuredCloneReader,
|
||||
closure: *mut raw::c_void,
|
||||
content: *mut raw::c_void,
|
||||
extra_data: u64,
|
||||
return_object: MutableHandleObject,
|
||||
) -> bool;
|
||||
fn detached(&self) -> Option<bool> { None }
|
||||
fn set_detached(&self, _value: bool) { }
|
||||
fn transferable(&self) -> bool { false }
|
||||
}
|
|
@ -462,13 +462,20 @@ 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);
|
||||
assert!(data.read(scope.upcast(), message.handle_mut()));
|
||||
MessageEvent::dispatch_jsval(
|
||||
target,
|
||||
scope.upcast(),
|
||||
message.handle(),
|
||||
Some(&origin),
|
||||
None,
|
||||
vec![],
|
||||
);
|
||||
},
|
||||
WorkerScriptMsg::Common(msg) => {
|
||||
self.upcast::<WorkerGlobalScope>().process_event(msg);
|
||||
|
@ -562,11 +569,13 @@ 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)?;
|
||||
rooted!(in(*cx) let transfer = UndefinedValue());
|
||||
let data = StructuredCloneData::write(*cx, message, transfer.handle())?;
|
||||
let worker = self.worker.borrow().as_ref().unwrap().clone();
|
||||
let pipeline_id = self.upcast::<GlobalScope>().pipeline_id();
|
||||
let pipeline_id = self.global().pipeline_id();
|
||||
let origin = self.global().origin().immutable().ascii_serialization();
|
||||
let task = Box::new(task!(post_worker_message: move || {
|
||||
Worker::handle_message(worker, data);
|
||||
Worker::handle_message(worker, origin, data);
|
||||
}));
|
||||
// TODO: Change this task source to a new `unshipped-port-message-queue` task source
|
||||
self.parent_sender
|
||||
|
|
|
@ -150,7 +150,8 @@ impl DissimilarOriginWindowMethods for DissimilarOriginWindow {
|
|||
|
||||
// Step 1-2, 6-8.
|
||||
// TODO(#12717): Should implement the `transfer` argument.
|
||||
let data = StructuredCloneData::write(*cx, message)?;
|
||||
rooted!(in(*cx) let transfer = UndefinedValue());
|
||||
let data = StructuredCloneData::write(*cx, message, transfer.handle())?;
|
||||
|
||||
// Step 9.
|
||||
self.post_message(origin, data);
|
||||
|
|
|
@ -236,6 +236,7 @@ impl EventSourceContext {
|
|||
DOMString::from(self.origin.clone()),
|
||||
None,
|
||||
event_source.last_event_id.borrow().clone(),
|
||||
vec![],
|
||||
)
|
||||
};
|
||||
// Step 7
|
||||
|
|
|
@ -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::task_source::TaskSourceName;
|
||||
|
@ -504,7 +505,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 +517,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 +541,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>() {
|
||||
|
@ -537,7 +550,7 @@ impl GlobalScope {
|
|||
if let Some(worker) = self.downcast::<WorkerGlobalScope>() {
|
||||
return worker.websocket_task_source();
|
||||
}
|
||||
unreachable!();
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
/// Evaluate JS code on this global scope.
|
||||
|
|
|
@ -185,7 +185,8 @@ impl History {
|
|||
// TODO: Step 4
|
||||
|
||||
// Step 5
|
||||
let serialized_data = StructuredCloneData::write(*cx, data)?.move_to_arraybuffer();
|
||||
rooted!(in(cx) let transfer = UndefinedValue());
|
||||
let serialized_data = StructuredCloneData::write(*cx, data, transfer.handle())?.move_to_arraybuffer();
|
||||
|
||||
let new_url: ServoUrl = match url {
|
||||
// Step 6
|
||||
|
|
60
components/script/dom/messagechannel.rs
Normal file
60
components/script/dom/messagechannel.rs
Normal file
|
@ -0,0 +1,60 @@
|
|||
/* 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::{Reflector, reflect_dom_object};
|
||||
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);
|
||||
|
||||
// Step 3
|
||||
port1.entangle(&port2);
|
||||
|
||||
// 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)
|
||||
}
|
||||
}
|
|
@ -14,11 +14,14 @@ use crate::dom::bindings::trace::RootedTraceableBox;
|
|||
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;
|
||||
use js::jsapi::{Heap, JSObject};
|
||||
use js::jsval::JSVal;
|
||||
use js::conversions::ToJSValConvertible;
|
||||
use js::jsapi::{Heap, JS_FreezeObject, JSContext, JSObject};
|
||||
use js::jsapi::HandleObject as RawHandleObject;
|
||||
use js::jsval::{JSVal, UndefinedValue};
|
||||
use js::rust::HandleValue;
|
||||
use servo_atoms::Atom;
|
||||
use std::ptr::NonNull;
|
||||
|
@ -31,6 +34,7 @@ pub struct MessageEvent {
|
|||
origin: DOMString,
|
||||
source: Option<Dom<WindowProxy>>,
|
||||
lastEventId: DOMString,
|
||||
ports: Vec<DomRoot<MessagePort>>,
|
||||
}
|
||||
|
||||
impl MessageEvent {
|
||||
|
@ -41,6 +45,7 @@ impl MessageEvent {
|
|||
DOMString::new(),
|
||||
None,
|
||||
DOMString::new(),
|
||||
vec![],
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -50,13 +55,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 +80,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 +108,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 +121,7 @@ impl MessageEvent {
|
|||
message: HandleValue,
|
||||
origin: Option<&str>,
|
||||
source: Option<&WindowProxy>,
|
||||
ports: Vec<DomRoot<MessagePort>>,
|
||||
) {
|
||||
let messageevent = MessageEvent::new(
|
||||
scope,
|
||||
|
@ -122,18 +132,19 @@ impl MessageEvent {
|
|||
DOMString::from(origin.unwrap_or("")),
|
||||
source,
|
||||
DOMString::new(),
|
||||
ports,
|
||||
);
|
||||
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 +156,24 @@ 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()
|
||||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
/// <https://html.spec.whatwg.org/multipage/#dom-messageevent-ports>
|
||||
unsafe fn Ports(&self, cx: *mut JSContext) -> JSVal {
|
||||
rooted!(in(cx) let mut ports = UndefinedValue());
|
||||
self.ports.to_jsval(cx, ports.handle_mut());
|
||||
|
||||
rooted!(in(cx) let obj = ports.to_object());
|
||||
JS_FreezeObject(cx, RawHandleObject::from(obj.handle()));
|
||||
*ports
|
||||
}
|
||||
}
|
||||
|
|
395
components/script/dom/messageport.rs
Normal file
395
components/script/dom/messageport.rs
Normal file
|
@ -0,0 +1,395 @@
|
|||
/* 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, Wrap};
|
||||
use crate::dom::bindings::conversions::{ToJSValConvertible, root_from_object};
|
||||
use crate::dom::bindings::error::{Error, ErrorResult};
|
||||
use crate::dom::bindings::inheritance::{Castable, HasParent};
|
||||
use crate::dom::bindings::refcounted::Trusted;
|
||||
use crate::dom::bindings::reflector::{DomObject, reflect_dom_object};
|
||||
use crate::dom::bindings::root::DomRoot;
|
||||
use crate::dom::bindings::structuredclone::StructuredCloneData;
|
||||
use crate::dom::bindings::trace::JSTraceable;
|
||||
use crate::dom::bindings::transferable::Transferable;
|
||||
use crate::dom::eventtarget::EventTarget;
|
||||
use crate::dom::globalscope::GlobalScope;
|
||||
use crate::dom::messageevent::MessageEvent;
|
||||
use crate::task_source::TaskSource;
|
||||
use crate::task_source::port_message::PortMessageQueue;
|
||||
use js::jsapi::{JSContext, JSStructuredCloneReader, JSObject, JSTracer, MutableHandleObject};
|
||||
use js::jsval::UndefinedValue;
|
||||
use js::rust::{CustomAutoRooterGuard, HandleValue};
|
||||
use servo_remutex::ReentrantMutex;
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::collections::VecDeque;
|
||||
use std::mem;
|
||||
use std::os::raw;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
|
||||
// FIXME: This is wrong, we need to figure out a better way of collecting message port objects per transfer
|
||||
thread_local! {
|
||||
pub static TRANSFERRED_MESSAGE_PORTS: RefCell<Vec<DomRoot<MessagePort>>> = RefCell::new(Vec::new())
|
||||
}
|
||||
|
||||
struct PortMessageTask {
|
||||
origin: String,
|
||||
data: Vec<u8>,
|
||||
}
|
||||
|
||||
pub struct MessagePortInternal {
|
||||
dom_port: RefCell<Option<Trusted<MessagePort>>>,
|
||||
port_message_queue: RefCell<PortMessageQueue>,
|
||||
enabled: Cell<bool>,
|
||||
has_been_shipped: Cell<bool>,
|
||||
entangled_port: RefCell<Option<Arc<ReentrantMutex<MessagePortInternal>>>>,
|
||||
pending_port_messages: RefCell<VecDeque<PortMessageTask>>,
|
||||
}
|
||||
|
||||
impl MessagePortInternal {
|
||||
fn new(port_message_queue: PortMessageQueue) -> MessagePortInternal {
|
||||
MessagePortInternal {
|
||||
dom_port: RefCell::new(None),
|
||||
port_message_queue: RefCell::new(port_message_queue),
|
||||
enabled: Cell::new(false),
|
||||
has_been_shipped: Cell::new(false),
|
||||
entangled_port: RefCell::new(None),
|
||||
pending_port_messages: RefCell::new(VecDeque::new()),
|
||||
}
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#dom-messageport-postmessage>
|
||||
// Step 7 substeps
|
||||
#[allow(unrooted_must_root)]
|
||||
fn process_pending_port_messages(&self) {
|
||||
let PortMessageTask { origin, data } = match self.pending_port_messages.borrow_mut().pop_front() {
|
||||
Some(task) => task,
|
||||
None => return,
|
||||
};
|
||||
|
||||
// Substep 1
|
||||
let final_target_port = self.dom_port.borrow().as_ref().unwrap().root();
|
||||
|
||||
// Substep 2
|
||||
let target_global = final_target_port.global();
|
||||
|
||||
// Substep 3-4
|
||||
rooted!(in(target_global.get_cx()) let mut message_clone = UndefinedValue());
|
||||
let deserialize_result = StructuredCloneData::Vector(data).read(
|
||||
&target_global,
|
||||
message_clone.handle_mut()
|
||||
);
|
||||
if !deserialize_result {
|
||||
return;
|
||||
}
|
||||
|
||||
// Substep 5
|
||||
let new_ports = TRANSFERRED_MESSAGE_PORTS.with(|list| {
|
||||
mem::replace(&mut *list.borrow_mut(), vec![])
|
||||
});
|
||||
|
||||
// Substep 6
|
||||
MessageEvent::dispatch_jsval(
|
||||
final_target_port.upcast(),
|
||||
&target_global,
|
||||
message_clone.handle(),
|
||||
Some(&origin),
|
||||
None,
|
||||
new_ports,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(DenyPublicFields, DomObject, MallocSizeOf)]
|
||||
#[must_root]
|
||||
#[repr(C)]
|
||||
pub struct MessagePort {
|
||||
eventtarget: EventTarget,
|
||||
detached: Cell<bool>,
|
||||
#[ignore_malloc_size_of = "Defined in std"]
|
||||
message_port_internal: Arc<ReentrantMutex<MessagePortInternal>>,
|
||||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
unsafe impl JSTraceable for MessagePort {
|
||||
unsafe fn trace(&self, trc: *mut JSTracer) {
|
||||
if !self.detached.get() {
|
||||
self.eventtarget.trace(trc);
|
||||
}
|
||||
// Otherwise, do nothing.
|
||||
}
|
||||
}
|
||||
|
||||
impl HasParent for MessagePort {
|
||||
type Parent = EventTarget;
|
||||
|
||||
fn as_parent(&self) -> &EventTarget {
|
||||
&self.eventtarget
|
||||
}
|
||||
}
|
||||
|
||||
impl MessagePort {
|
||||
fn new_inherited(global: &GlobalScope) -> MessagePort {
|
||||
MessagePort {
|
||||
eventtarget: EventTarget::new_inherited(),
|
||||
detached: Cell::new(false),
|
||||
message_port_internal: Arc::new(
|
||||
ReentrantMutex::new(
|
||||
MessagePortInternal::new(global.port_message_queue().clone())
|
||||
)
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
fn new_transferred(message_port_internal: Arc<ReentrantMutex<MessagePortInternal>>) -> MessagePort {
|
||||
MessagePort {
|
||||
eventtarget: EventTarget::new_inherited(),
|
||||
detached: Cell::new(false),
|
||||
message_port_internal,
|
||||
}
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#create-a-new-messageport-object>
|
||||
pub fn new(owner: &GlobalScope) -> DomRoot<MessagePort> {
|
||||
let message_port = reflect_dom_object(Box::new(MessagePort::new_inherited(owner)), owner, Wrap);
|
||||
{
|
||||
let internal = message_port.message_port_internal.lock().unwrap();
|
||||
*internal.dom_port.borrow_mut() = Some(Trusted::new(&*message_port));
|
||||
}
|
||||
message_port
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#entangle>
|
||||
pub fn entangle(&self, other: &MessagePort) {
|
||||
{
|
||||
let internal = self.message_port_internal.lock().unwrap();
|
||||
*internal.entangled_port.borrow_mut() = Some(other.message_port_internal.clone());
|
||||
}
|
||||
let internal = other.message_port_internal.lock().unwrap();
|
||||
*internal.entangled_port.borrow_mut() = Some(self.message_port_internal.clone());
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#dom-messageport-postmessage>
|
||||
// Step 7 substeps
|
||||
fn process_pending_port_messages(&self) {
|
||||
if self.detached.get() { return; }
|
||||
let internal = self.message_port_internal.lock().unwrap();
|
||||
internal.process_pending_port_messages();
|
||||
}
|
||||
}
|
||||
|
||||
impl Transferable for MessagePort {
|
||||
/// <https://html.spec.whatwg.org/multipage/#message-ports:transfer-steps>
|
||||
#[allow(unsafe_code)]
|
||||
fn transfer(
|
||||
&self,
|
||||
_closure: *mut raw::c_void,
|
||||
content: *mut *mut raw::c_void,
|
||||
extra_data: *mut u64
|
||||
) -> bool {
|
||||
{
|
||||
let internal = self.message_port_internal.lock().unwrap();
|
||||
// Step 1
|
||||
internal.has_been_shipped.set(true);
|
||||
|
||||
// Step 3
|
||||
if let Some(ref other_port) = *internal.entangled_port.borrow() {
|
||||
let entangled_internal = other_port.lock().unwrap();
|
||||
// Substep 1
|
||||
entangled_internal.has_been_shipped.set(true);
|
||||
}; // This line MUST contain a semicolon, due to the strict drop check rule
|
||||
}
|
||||
|
||||
unsafe {
|
||||
// Steps 2, 3.2 and 4
|
||||
*content = Arc::into_raw(self.message_port_internal.clone()) as *mut raw::c_void;
|
||||
|
||||
*extra_data = 0;
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
/// https://html.spec.whatwg.org/multipage/#message-ports:transfer-receiving-steps
|
||||
#[allow(unrooted_must_root, unsafe_code)]
|
||||
fn transfer_receive(
|
||||
cx: *mut JSContext,
|
||||
_r: *mut JSStructuredCloneReader,
|
||||
_closure: *mut raw::c_void,
|
||||
content: *mut raw::c_void,
|
||||
_extra_data: u64,
|
||||
return_object: MutableHandleObject
|
||||
) -> bool {
|
||||
let internal = unsafe { Arc::from_raw(content as *const ReentrantMutex<MessagePortInternal>) };
|
||||
let value = MessagePort::new_transferred(internal);
|
||||
|
||||
// Step 2
|
||||
let owner = unsafe { GlobalScope::from_context(cx) };
|
||||
let message_port = reflect_dom_object(Box::new(value), &*owner, Wrap);
|
||||
|
||||
{
|
||||
let internal = message_port.message_port_internal.lock().unwrap();
|
||||
|
||||
// Step 1
|
||||
internal.has_been_shipped.set(true);
|
||||
|
||||
let dom_port = Trusted::new(&*message_port);
|
||||
internal.enabled.set(false);
|
||||
*internal.dom_port.borrow_mut() = Some(dom_port);
|
||||
*internal.port_message_queue.borrow_mut() = owner.port_message_queue().clone();
|
||||
}
|
||||
return_object.set(message_port.reflector().rootable().get());
|
||||
TRANSFERRED_MESSAGE_PORTS.with(|list| {
|
||||
list.borrow_mut().push(message_port);
|
||||
});
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
fn detached(&self) -> Option<bool> {
|
||||
Some(self.detached.get())
|
||||
}
|
||||
|
||||
fn set_detached(&self, value: bool) {
|
||||
self.detached.set(value);
|
||||
}
|
||||
|
||||
fn transferable(&self) -> bool {
|
||||
!self.detached.get()
|
||||
}
|
||||
}
|
||||
|
||||
impl MessagePortMethods for MessagePort {
|
||||
#[allow(unsafe_code)]
|
||||
/// <https://html.spec.whatwg.org/multipage/#dom-messageport-postmessage>
|
||||
unsafe fn PostMessage(
|
||||
&self,
|
||||
cx: *mut JSContext,
|
||||
message: HandleValue,
|
||||
transfer: CustomAutoRooterGuard<Option<Vec<*mut JSObject>>>,
|
||||
) -> ErrorResult {
|
||||
if self.detached.get() { return Ok(()); }
|
||||
let internal = self.message_port_internal.lock().unwrap();
|
||||
// Step 1
|
||||
let target_port = internal.entangled_port.borrow();
|
||||
|
||||
// Step 3
|
||||
let mut doomed = false;
|
||||
|
||||
rooted!(in(cx) let mut val = UndefinedValue());
|
||||
let transfer = match *transfer {
|
||||
Some(ref vec) => {
|
||||
let ports = vec.iter().filter_map(|&obj| root_from_object::<MessagePort>(obj).ok());
|
||||
for port in ports {
|
||||
// Step 2
|
||||
if Arc::ptr_eq(&port.message_port_internal, &self.message_port_internal) {
|
||||
return Err(Error::DataClone);
|
||||
}
|
||||
|
||||
// Step 4
|
||||
if let Some(target) = target_port.as_ref() {
|
||||
if Arc::ptr_eq(&port.message_port_internal, target) {
|
||||
doomed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vec.to_jsval(cx, val.handle_mut());
|
||||
val
|
||||
}
|
||||
None => {
|
||||
Vec::<*mut JSObject>::new().to_jsval(cx, val.handle_mut());
|
||||
val
|
||||
}
|
||||
};
|
||||
|
||||
// Step 5
|
||||
let data = StructuredCloneData::write(cx, message, transfer.handle())?.move_to_arraybuffer();
|
||||
|
||||
// Step 6
|
||||
if target_port.is_none() || doomed { return Ok(()); }
|
||||
|
||||
// Step 7
|
||||
let task = PortMessageTask {
|
||||
origin: self.global().origin().immutable().ascii_serialization(),
|
||||
data,
|
||||
};
|
||||
|
||||
{
|
||||
let target_port = target_port.as_ref().unwrap();
|
||||
let target_internal = target_port.lock().unwrap();
|
||||
target_internal.pending_port_messages.borrow_mut().push_back(task);
|
||||
|
||||
if target_internal.enabled.get() {
|
||||
let target_port = target_port.clone();
|
||||
let _ = target_internal.port_message_queue.borrow().queue(
|
||||
task!(process_pending_port_messages: move || {
|
||||
let internal = target_port.lock().unwrap();
|
||||
internal.process_pending_port_messages();
|
||||
}),
|
||||
&self.global()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#dom-messageport-start>
|
||||
fn Start(&self) {
|
||||
let len = {
|
||||
let internal = self.message_port_internal.lock().unwrap();
|
||||
if internal.enabled.get() {
|
||||
return;
|
||||
}
|
||||
internal.enabled.set(true);
|
||||
let messages = internal.pending_port_messages.borrow();
|
||||
messages.len()
|
||||
};
|
||||
|
||||
let global = self.global();
|
||||
for _ in 0..len {
|
||||
let port = Trusted::new(self);
|
||||
let _ = global.port_message_queue().queue(
|
||||
task!(process_pending_port_messages: move || {
|
||||
let this = port.root();
|
||||
this.process_pending_port_messages();
|
||||
}),
|
||||
&global
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#dom-messageport-close>
|
||||
fn Close(&self) {
|
||||
// Step 1
|
||||
self.detached.set(true);
|
||||
|
||||
// Step 2
|
||||
let maybe_port = {
|
||||
let internal = self.message_port_internal.lock().unwrap();
|
||||
let mut maybe_port = internal.entangled_port.borrow_mut();
|
||||
maybe_port.take()
|
||||
};
|
||||
|
||||
if let Some(other) = maybe_port {
|
||||
let other_internal = other.lock().unwrap();
|
||||
*other_internal.entangled_port.borrow_mut() = None;
|
||||
}
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#handler-messageport-onmessage>
|
||||
fn GetOnmessage(&self) -> Option<Rc<EventHandlerNonNull>> {
|
||||
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>>) {
|
||||
self.Start();
|
||||
let eventtarget = self.upcast::<EventTarget>();
|
||||
eventtarget.set_event_handler_common("message", listener)
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -19,6 +19,8 @@ use crate::dom::globalscope::GlobalScope;
|
|||
use crate::script_runtime::JSContext;
|
||||
use crate::task::TaskOnce;
|
||||
use dom_struct::dom_struct;
|
||||
use js::jsapi::JSContext;
|
||||
use js::jsval::UndefinedValue;
|
||||
use js::rust::HandleValue;
|
||||
use script_traits::{DOMMessage, ScriptMsg};
|
||||
use servo_url::ServoUrl;
|
||||
|
@ -97,14 +99,18 @@ impl ServiceWorkerMethods for ServiceWorker {
|
|||
return Err(Error::InvalidState);
|
||||
}
|
||||
// Step 7
|
||||
let data = StructuredCloneData::write(*cx, message)?;
|
||||
let msg_vec = DOMMessage(data.move_to_arraybuffer());
|
||||
rooted!(in(*cx) let transfer = UndefinedValue());
|
||||
let data = StructuredCloneData::write(*cx, message, transfer.handle())?;
|
||||
let msg_vec = DOMMessage {
|
||||
origin: self.global().origin().immutable().ascii_serialization(),
|
||||
data: data.move_to_arraybuffer(),
|
||||
};
|
||||
let _ = self
|
||||
.global()
|
||||
.script_to_constellation_chan()
|
||||
.send(ScriptMsg::ForwardDOMMessage(
|
||||
msg_vec,
|
||||
self.scope_url.clone(),
|
||||
self.scope_url.clone()
|
||||
));
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -409,12 +409,12 @@ 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());
|
||||
assert!(data.read(scope.upcast(), message.handle_mut()));
|
||||
ExtendableMessageEvent::dispatch_jsval(target, scope.upcast(), message.handle());
|
||||
},
|
||||
CommonWorker(WorkerScriptMsg::Common(msg)) => {
|
||||
|
|
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;
|
||||
|
|
17
components/script/dom/webidls/MessagePort.webidl
Normal file
17
components/script/dom/webidls/MessagePort.webidl
Normal file
|
@ -0,0 +1,17 @@
|
|||
/* 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, optional sequence<object> transfer /*= []*/);
|
||||
void start();
|
||||
void close();
|
||||
|
||||
// event handlers
|
||||
attribute EventHandler onmessage;
|
||||
};
|
|
@ -63,9 +63,8 @@
|
|||
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 /*= []*/);
|
||||
|
||||
// also has obsolete members
|
||||
};
|
||||
|
|
|
@ -598,6 +598,7 @@ impl TaskOnce for MessageReceivedTask {
|
|||
message.handle(),
|
||||
Some(&ws.origin().ascii_serialization()),
|
||||
None,
|
||||
vec![],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ use crate::dom::bindings::codegen::Bindings::WindowBinding::{
|
|||
};
|
||||
use crate::dom::bindings::codegen::Bindings::WindowBinding::{ScrollBehavior, ScrollToOptions};
|
||||
use crate::dom::bindings::codegen::UnionTypes::RequestOrUSVString;
|
||||
use crate::dom::bindings::conversions::ToJSValConvertible;
|
||||
use crate::dom::bindings::error::{Error, ErrorResult, Fallible};
|
||||
use crate::dom::bindings::inheritance::Castable;
|
||||
use crate::dom::bindings::num::Finite;
|
||||
|
@ -43,6 +44,7 @@ use crate::dom::location::Location;
|
|||
use crate::dom::mediaquerylist::{MediaQueryList, MediaQueryListMatchState};
|
||||
use crate::dom::mediaquerylistevent::MediaQueryListEvent;
|
||||
use crate::dom::messageevent::MessageEvent;
|
||||
use crate::dom::messageport::TRANSFERRED_MESSAGE_PORTS;
|
||||
use crate::dom::navigator::Navigator;
|
||||
use crate::dom::node::{document_from_node, from_untrusted_node_address, Node, NodeDamage};
|
||||
use crate::dom::performance::Performance;
|
||||
|
@ -80,12 +82,13 @@ use euclid::{Point2D, Rect, Scale, Size2D, Vector2D};
|
|||
use ipc_channel::ipc::{channel, IpcSender};
|
||||
use ipc_channel::router::ROUTER;
|
||||
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::{CustomAutoRooterGuard, HandleValue};
|
||||
use media::WindowGLContext;
|
||||
use msg::constellation_msg::PipelineId;
|
||||
use net_traits::image_cache::{ImageCache, ImageResponder, ImageResponse};
|
||||
|
@ -969,12 +972,18 @@ impl WindowMethods for Window {
|
|||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/#dom-window-postmessage
|
||||
fn PostMessage(&self, cx: JSContext, message: HandleValue, origin: DOMString) -> ErrorResult {
|
||||
fn PostMessage(
|
||||
&self,
|
||||
cx: JSContext,
|
||||
message: HandleValue,
|
||||
origin: USVString,
|
||||
transfer: CustomAutoRooterGuard<Option<Vec<*mut JSObject>>>,
|
||||
) -> ErrorResult {
|
||||
let source_global = GlobalScope::incumbent().expect("no incumbent global??");
|
||||
let source = source_global.as_window();
|
||||
|
||||
// Step 3-5.
|
||||
let origin = match &origin[..] {
|
||||
let origin = match &origin.0[..] {
|
||||
"*" => None,
|
||||
"/" => Some(source.Document().origin().immutable().clone()),
|
||||
url => match ServoUrl::parse(&url) {
|
||||
|
@ -984,8 +993,10 @@ impl WindowMethods for Window {
|
|||
};
|
||||
|
||||
// Step 1-2, 6-8.
|
||||
// TODO(#12717): Should implement the `transfer` argument.
|
||||
let data = StructuredCloneData::write(*cx, message)?;
|
||||
rooted!(in(*cx) let mut val = UndefinedValue());
|
||||
(*transfer).as_ref().unwrap_or(&Vec::new()).to_jsval(*cx, val.handle_mut());
|
||||
|
||||
let data = StructuredCloneData::write(*cx, message, val.handle())?;
|
||||
|
||||
// Step 9.
|
||||
self.post_message(origin, &*source.window_proxy(), data);
|
||||
|
@ -2342,10 +2353,11 @@ impl Window {
|
|||
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,13 +2367,15 @@ 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(
|
||||
assert!(serialize_with_transfer_result.read(
|
||||
this.upcast(),
|
||||
message_clone.handle_mut(),
|
||||
);
|
||||
));
|
||||
|
||||
// Step 7.6.
|
||||
// TODO: MessagePort array.
|
||||
let new_ports = TRANSFERRED_MESSAGE_PORTS.with(|list| {
|
||||
mem::replace(&mut *list.borrow_mut(), vec![])
|
||||
});
|
||||
|
||||
// Step 7.7.
|
||||
// TODO(#12719): Set the other attributes.
|
||||
|
@ -2369,8 +2383,9 @@ impl Window {
|
|||
this.upcast(),
|
||||
this.upcast(),
|
||||
message_clone.handle(),
|
||||
None,
|
||||
Some(&document.origin().immutable().ascii_serialization()),
|
||||
Some(&*source),
|
||||
new_ports,
|
||||
);
|
||||
});
|
||||
// FIXME(nox): Why are errors silenced here?
|
||||
|
|
|
@ -137,7 +137,11 @@ impl Worker {
|
|||
self.terminated.get()
|
||||
}
|
||||
|
||||
pub fn handle_message(address: TrustedWorkerAddress, data: StructuredCloneData) {
|
||||
pub fn handle_message(
|
||||
address: TrustedWorkerAddress,
|
||||
origin: String,
|
||||
data: StructuredCloneData,
|
||||
) {
|
||||
let worker = address.root();
|
||||
|
||||
if worker.is_terminated() {
|
||||
|
@ -148,8 +152,8 @@ 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);
|
||||
assert!(data.read(&global, message.handle_mut()));
|
||||
MessageEvent::dispatch_jsval(target, &global, message.handle(), Some(&origin), None, vec![]);
|
||||
}
|
||||
|
||||
pub fn dispatch_simple_error(address: TrustedWorkerAddress) {
|
||||
|
@ -161,14 +165,18 @@ impl Worker {
|
|||
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)?;
|
||||
rooted!(in(*cx) let transfer = UndefinedValue());
|
||||
let data = StructuredCloneData::write(*cx, message, transfer.handle())?;
|
||||
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().ascii_serialization(),
|
||||
data,
|
||||
},
|
||||
));
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
|
|
@ -115,6 +115,7 @@ pub enum ScriptThreadEventCategory {
|
|||
ImageCacheMsg,
|
||||
InputEvent,
|
||||
NetworkEvent,
|
||||
PortMessage,
|
||||
Resize,
|
||||
ScriptEvent,
|
||||
SetScrollState,
|
||||
|
|
|
@ -81,6 +81,7 @@ use crate::task_source::history_traversal::HistoryTraversalTaskSource;
|
|||
use crate::task_source::media_element::MediaElementTaskSource;
|
||||
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::user_interaction::UserInteractionTaskSource;
|
||||
use crate::task_source::websocket::WebsocketTaskSource;
|
||||
|
@ -566,6 +567,8 @@ pub struct ScriptThread {
|
|||
|
||||
performance_timeline_task_sender: Box<dyn ScriptChan>,
|
||||
|
||||
port_message_sender: Box<dyn ScriptChan>,
|
||||
|
||||
remote_event_task_sender: Box<dyn ScriptChan>,
|
||||
|
||||
/// A channel to hand out to threads that need to respond to a message from the script thread.
|
||||
|
@ -1296,6 +1299,7 @@ impl ScriptThread {
|
|||
media_element_task_sender: chan.clone(),
|
||||
user_interaction_task_sender: chan.clone(),
|
||||
networking_task_sender: boxed_script_sender.clone(),
|
||||
port_message_sender: boxed_script_sender.clone(),
|
||||
file_reading_task_sender: boxed_script_sender.clone(),
|
||||
performance_timeline_task_sender: boxed_script_sender.clone(),
|
||||
remote_event_task_sender: boxed_script_sender.clone(),
|
||||
|
@ -1656,6 +1660,7 @@ impl ScriptThread {
|
|||
ScriptThreadEventCategory::PerformanceTimelineTask => {
|
||||
ScriptHangAnnotation::PerformanceTimelineTask
|
||||
},
|
||||
ScriptThreadEventCategory::PortMessage => ScriptHangAnnotation::PortMessage,
|
||||
};
|
||||
self.background_hang_monitor
|
||||
.notify_activity(HangAnnotation::Script(hang_annotation));
|
||||
|
@ -1756,6 +1761,7 @@ impl ScriptThread {
|
|||
ScriptThreadEventCategory::ImageCacheMsg => ProfilerCategory::ScriptImageCacheMsg,
|
||||
ScriptThreadEventCategory::InputEvent => ProfilerCategory::ScriptInputEvent,
|
||||
ScriptThreadEventCategory::NetworkEvent => ProfilerCategory::ScriptNetworkEvent,
|
||||
ScriptThreadEventCategory::PortMessage => ProfilerCategory::ScriptPortMessage,
|
||||
ScriptThreadEventCategory::Resize => ProfilerCategory::ScriptResize,
|
||||
ScriptThreadEventCategory::ScriptEvent => ProfilerCategory::ScriptEvent,
|
||||
ScriptThreadEventCategory::SetScrollState => ProfilerCategory::ScriptSetScrollState,
|
||||
|
@ -2780,6 +2786,10 @@ impl ScriptThread {
|
|||
NetworkingTaskSource(self.networking_task_sender.clone(), pipeline_id)
|
||||
}
|
||||
|
||||
pub fn port_message_queue(&self, pipeline_id: PipelineId) -> PortMessageQueue {
|
||||
PortMessageQueue(self.port_message_sender.clone(), pipeline_id)
|
||||
}
|
||||
|
||||
pub fn file_reading_task_source(&self, pipeline_id: PipelineId) -> FileReadingTaskSource {
|
||||
FileReadingTaskSource(self.file_reading_task_sender.clone(), pipeline_id)
|
||||
}
|
||||
|
@ -3177,6 +3187,7 @@ impl ScriptThread {
|
|||
self.networking_task_source(incomplete.pipeline_id),
|
||||
self.performance_timeline_task_source(incomplete.pipeline_id)
|
||||
.clone(),
|
||||
self.port_message_queue(incomplete.pipeline_id),
|
||||
self.user_interaction_task_source(incomplete.pipeline_id),
|
||||
self.remote_event_task_source(incomplete.pipeline_id),
|
||||
self.websocket_task_source(incomplete.pipeline_id),
|
||||
|
|
|
@ -135,10 +135,10 @@ impl ServiceWorkerManager {
|
|||
}
|
||||
|
||||
fn forward_message(&self, msg: DOMMessage, sender: &Sender<ServiceWorkerScriptMsg>) {
|
||||
let DOMMessage(data) = msg;
|
||||
let DOMMessage { origin, data } = msg;
|
||||
let data = StructuredCloneData::Vector(data);
|
||||
let _ = sender.send(ServiceWorkerScriptMsg::CommonWorker(
|
||||
WorkerScriptMsg::DOMMessage(data),
|
||||
WorkerScriptMsg::DOMMessage { origin, data },
|
||||
));
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ use crate::task_source::history_traversal::HistoryTraversalTaskSource;
|
|||
use crate::task_source::media_element::MediaElementTaskSource;
|
||||
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::user_interaction::UserInteractionTaskSource;
|
||||
use crate::task_source::websocket::WebsocketTaskSource;
|
||||
|
@ -47,6 +48,8 @@ pub struct TaskManager {
|
|||
#[ignore_malloc_size_of = "task sources are hard"]
|
||||
performance_timeline_task_source: PerformanceTimelineTaskSource,
|
||||
#[ignore_malloc_size_of = "task sources are hard"]
|
||||
port_message_queue: PortMessageQueue,
|
||||
#[ignore_malloc_size_of = "task sources are hard"]
|
||||
user_interaction_task_source: UserInteractionTaskSource,
|
||||
#[ignore_malloc_size_of = "task sources are hard"]
|
||||
remote_event_task_source: RemoteEventTaskSource,
|
||||
|
@ -62,6 +65,7 @@ impl TaskManager {
|
|||
media_element_task_source: MediaElementTaskSource,
|
||||
networking_task_source: NetworkingTaskSource,
|
||||
performance_timeline_task_source: PerformanceTimelineTaskSource,
|
||||
port_message_queue: PortMessageQueue,
|
||||
user_interaction_task_source: UserInteractionTaskSource,
|
||||
remote_event_task_source: RemoteEventTaskSource,
|
||||
websocket_task_source: WebsocketTaskSource,
|
||||
|
@ -73,6 +77,7 @@ impl TaskManager {
|
|||
media_element_task_source,
|
||||
networking_task_source,
|
||||
performance_timeline_task_source,
|
||||
port_message_queue,
|
||||
user_interaction_task_source,
|
||||
remote_event_task_source,
|
||||
websocket_task_source,
|
||||
|
@ -136,6 +141,14 @@ impl TaskManager {
|
|||
PerformanceTimeline
|
||||
);
|
||||
|
||||
task_source_functions!(
|
||||
self,
|
||||
port_message_queue_with_canceller,
|
||||
port_message_queue,
|
||||
PortMessageQueue,
|
||||
PortMessage
|
||||
);
|
||||
|
||||
task_source_functions!(
|
||||
self,
|
||||
remote_event_task_source_with_canceller,
|
||||
|
|
|
@ -8,6 +8,7 @@ pub mod history_traversal;
|
|||
pub mod media_element;
|
||||
pub mod networking;
|
||||
pub mod performance_timeline;
|
||||
pub mod port_message;
|
||||
pub mod remote_event;
|
||||
pub mod user_interaction;
|
||||
pub mod websocket;
|
||||
|
@ -28,6 +29,7 @@ pub enum TaskSourceName {
|
|||
HistoryTraversal,
|
||||
Networking,
|
||||
PerformanceTimeline,
|
||||
PortMessage,
|
||||
UserInteraction,
|
||||
RemoteEvent,
|
||||
MediaElement,
|
||||
|
|
45
components/script/task_source/port_message.rs
Normal file
45
components/script/task_source/port_message.rs
Normal file
|
@ -0,0 +1,45 @@
|
|||
/* 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::script_runtime::{CommonScriptMsg, ScriptChan, ScriptThreadEventCategory};
|
||||
use crate::task::{TaskCanceller, TaskOnce};
|
||||
use crate::task_source::{TaskSource, TaskSourceName};
|
||||
use msg::constellation_msg::PipelineId;
|
||||
use std::fmt;
|
||||
|
||||
#[derive(JSTraceable)]
|
||||
pub struct PortMessageQueue(pub Box<ScriptChan + Send + 'static>, pub PipelineId);
|
||||
|
||||
impl Clone for PortMessageQueue {
|
||||
fn clone(&self) -> PortMessageQueue {
|
||||
PortMessageQueue(self.0.clone(), self.1.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for PortMessageQueue {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "PortMessageQueue(...)")
|
||||
}
|
||||
}
|
||||
|
||||
impl TaskSource for PortMessageQueue {
|
||||
const NAME: TaskSourceName = TaskSourceName::PortMessage;
|
||||
|
||||
fn queue_with_canceller<T>(
|
||||
&self,
|
||||
task: T,
|
||||
canceller: &TaskCanceller,
|
||||
) -> Result<(), ()>
|
||||
where
|
||||
T: TaskOnce + 'static,
|
||||
{
|
||||
let msg = CommonScriptMsg::Task(
|
||||
ScriptThreadEventCategory::PortMessage,
|
||||
Box::new(canceller.wrap_task(task)),
|
||||
Some(self.1),
|
||||
Self::NAME,
|
||||
);
|
||||
self.0.send(msg).map_err(|_| ())
|
||||
}
|
||||
}
|
|
@ -284,7 +284,12 @@ pub struct ScopeThings {
|
|||
|
||||
/// Message that gets passed to service worker scope on postMessage
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct DOMMessage(pub Vec<u8>);
|
||||
pub struct DOMMessage {
|
||||
/// The origin of the message
|
||||
pub origin: String,
|
||||
/// The payload of the message
|
||||
pub data: Vec<u8>,
|
||||
}
|
||||
|
||||
/// Channels to allow service worker manager to communicate with constellation and resource thread
|
||||
pub struct SWManagerSenders {
|
||||
|
|
|
@ -167,6 +167,8 @@ skip: true
|
|||
skip: false
|
||||
[WebIDL]
|
||||
skip: false
|
||||
[webmessaging]
|
||||
skip: false
|
||||
[websockets]
|
||||
skip: false
|
||||
[webstorage]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue