Skip to content

Commit

Permalink
begin messageport, transferable objects, impl
Browse files Browse the repository at this point in the history
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
  • Loading branch information
KiChjang authored and gterzian committed Oct 19, 2019
1 parent 605ddbe commit c3b17c1
Show file tree
Hide file tree
Showing 37 changed files with 801 additions and 81 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions components/msg/constellation_msg.rs
Expand Up @@ -419,6 +419,7 @@ pub enum ScriptHangAnnotation {
ExitFullscreen,
WebVREvent,
PerformanceTimelineTask,
PortMessage,
}

#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
Expand Down
1 change: 1 addition & 0 deletions components/profile/time.rs
Expand Up @@ -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",
Expand Down
1 change: 1 addition & 0 deletions components/profile_traits/time.rs
Expand Up @@ -108,6 +108,7 @@ pub enum ProfilerCategory {
ScriptWorkletEvent = 0x7a,
ScriptPerformanceEvent = 0x7b,
ScriptHistoryEvent = 0x7c,
ScriptPortMessage = 0x7d,
TimeToFirstPaint = 0x80,
TimeToFirstContentfulPaint = 0x81,
TimeToInteractive = 0x82,
Expand Down
1 change: 1 addition & 0 deletions components/script/Cargo.toml
Expand Up @@ -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"] }
Expand Down
5 changes: 4 additions & 1 deletion components/script/dom/abstractworker.rs
Expand Up @@ -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> {
Expand Down
2 changes: 1 addition & 1 deletion components/script/dom/abstractworkerglobalscope.rs
Expand Up @@ -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!"),
}
}
}
Expand Down
1 change: 1 addition & 0 deletions components/script/dom/bindings/mod.rs
Expand Up @@ -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;
Expand Down
91 changes: 61 additions & 30 deletions components/script/dom/bindings/structuredclone.rs
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
}

Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -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,
Expand All @@ -275,7 +299,7 @@ impl StructuredCloneData {
policy,
&STRUCTURED_CLONE_CALLBACKS,
ptr::null_mut(),
HandleValue::undefined(),
transfer,
);
if !result {
JS_ClearPendingException(cx);
Expand Down Expand Up @@ -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 };
Expand All @@ -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)
},
}
}
}
}
Expand Down
29 changes: 29 additions & 0 deletions components/script/dom/bindings/transferable.rs
@@ -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 }
}
21 changes: 15 additions & 6 deletions components/script/dom/dedicatedworkerglobalscope.rs
Expand Up @@ -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);
Expand Down Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion components/script/dom/dissimilaroriginwindow.rs
Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions components/script/dom/eventsource.rs
Expand Up @@ -236,6 +236,7 @@ impl EventSourceContext {
DOMString::from(self.origin.clone()),
None,
event_source.last_event_id.borrow().clone(),
vec![],
)
};
// Step 7
Expand Down
21 changes: 17 additions & 4 deletions components/script/dom/globalscope.rs
Expand Up @@ -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;
Expand Down Expand Up @@ -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>() {
Expand All @@ -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>() {
Expand All @@ -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>() {
Expand All @@ -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.
Expand Down
3 changes: 2 additions & 1 deletion components/script/dom/history.rs
Expand Up @@ -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
Expand Down

0 comments on commit c3b17c1

Please sign in to comment.