Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement MessagePort and MessageChannel #16622

Closed
wants to merge 15 commits into from
Closed
Changes from all commits
Commits
File filter...
Filter file types
Jump to…
Jump to file
Failed to load files.

Always

Just for now

Some generated files are not rendered by default. Learn more.

@@ -337,6 +337,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,
@@ -99,6 +99,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"}
smallvec = { version = "0.6", features = ["std", "union"] }
style = {path = "../style", features = ["servo"]}
@@ -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;
@@ -9,8 +9,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;
@@ -43,6 +45,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,
}

@@ -188,26 +191,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
}

@@ -255,7 +275,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,
@@ -274,7 +298,7 @@ impl StructuredCloneData {
policy,
&STRUCTURED_CLONE_CALLBACKS,
ptr::null_mut(),
HandleValue::undefined(),
transfer,
);
if !result {
JS_ClearPendingException(cx);
@@ -305,7 +329,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 globalhandle = global.reflector().get_jsobject();
let _ac = JSAutoRealm::new(cx, globalhandle.get());
@@ -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)
},
}
}
}
}
@@ -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 }
}
@@ -459,13 +459,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 = JSAutoRealm::new(scope.get_cx(), scope.reflector().get_jsobject().get());
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);
@@ -560,11 +567,13 @@ impl DedicatedWorkerGlobalScopeMethods for DedicatedWorkerGlobalScope {
#[allow(unsafe_code)]
// https://html.spec.whatwg.org/multipage/#dom-dedicatedworkerglobalscope-postmessage
unsafe fn PostMessage(&self, cx: *mut 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 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
@@ -154,7 +154,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())?;

This comment has been minimized.

Copy link
@gterzian

gterzian Jun 26, 2018

Member

could the TODO on line 160 now be removed?

This comment has been minimized.

Copy link
@KiChjang

KiChjang Jul 21, 2018

Author Member

I don't think so, because the transfer argument is undefined. The proper way to do this would be to actually obtain them from the arguments passed to window.postMessage.

This comment has been minimized.

Copy link
@gterzian

gterzian Jul 22, 2018

Member

Ok, I see


// Step 9.
self.post_message(origin, data);
@@ -240,6 +240,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;
@@ -489,7 +490,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>() {
@@ -501,7 +502,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>() {
@@ -513,7 +526,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>() {
@@ -522,7 +535,7 @@ impl GlobalScope {
if let Some(worker) = self.downcast::<WorkerGlobalScope>() {
return worker.websocket_task_source();
}
unreachable!();
unreachable!()
}

/// Evaluate JS code on this global scope.
@@ -184,7 +184,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
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.