auto merge of #4057 : jdm/servo/refcountdom, r=Ms2ger

This replaces the specialized TrustedXHRAddress and TrustedWorkerAddress code that was used for the same purpose. A non-zero refcount pins the given DOM object's reflector and prevents it from being GCed even when there are no other outstanding references visible to SpiderMonkey. This will enable us to implement asynchronous operations that refer to particular DOM objects (such as "queue a task to fire a simple event named load at the iframe element" from the spec) safely and conveniently, and paves the way for things like asynchronous network responses.

Some concerns about the resulting size of XHR progress messages have been expressed, but I believe optimizations to reduce that can be implemented in subsequent PRs.

r? @Ms2ger - note in particular the changes to the worker lifetime code. I couldn't figure out how to achieve an identical lifetime to the previous addref/release pairing, and I also was having trouble figuring out why the existing setup was safe. The new implementation now holds the main script task Worker object alive via the TrustedWorkerAddress field in the dedicated worker global scope, which is a significant difference.
This commit is contained in:
bors-servo 2014-12-29 11:57:45 -07:00
commit 2c259f477c
13 changed files with 423 additions and 254 deletions

View file

@ -10,7 +10,7 @@
use dom::bindings::conversions::FromJSValConvertible; use dom::bindings::conversions::FromJSValConvertible;
use dom::bindings::js::{JS, JSRef, Root}; use dom::bindings::js::{JS, JSRef, Root};
use dom::bindings::utils::{Reflectable, Reflector}; use dom::bindings::utils::{Reflectable, Reflector};
use dom::workerglobalscope::WorkerGlobalScope; use dom::workerglobalscope::{WorkerGlobalScope, WorkerGlobalScopeHelpers};
use dom::window; use dom::window;
use script_task::ScriptChan; use script_task::ScriptChan;
@ -81,7 +81,7 @@ impl<'a> GlobalRef<'a> {
/// `ScriptChan` used to send messages to the event loop of this global's /// `ScriptChan` used to send messages to the event loop of this global's
/// thread. /// thread.
pub fn script_chan<'b>(&'b self) -> &'b ScriptChan { pub fn script_chan(&self) -> Box<ScriptChan+Send> {
match *self { match *self {
GlobalRef::Window(ref window) => window.script_chan(), GlobalRef::Window(ref window) => window.script_chan(),
GlobalRef::Worker(ref worker) => worker.script_chan(), GlobalRef::Worker(ref worker) => worker.script_chan(),

View file

@ -48,8 +48,6 @@
use dom::bindings::trace::JSTraceable; use dom::bindings::trace::JSTraceable;
use dom::bindings::utils::{Reflector, Reflectable}; use dom::bindings::utils::{Reflector, Reflectable};
use dom::node::Node; use dom::node::Node;
use dom::xmlhttprequest::{XMLHttpRequest, TrustedXHRAddress};
use dom::worker::{Worker, TrustedWorkerAddress};
use js::jsapi::JSObject; use js::jsapi::JSObject;
use js::jsval::JSVal; use js::jsval::JSVal;
use layout_interface::TrustedNodeAddress; use layout_interface::TrustedNodeAddress;
@ -142,24 +140,6 @@ impl JS<Node> {
} }
} }
impl JS<XMLHttpRequest> {
pub unsafe fn from_trusted_xhr_address(inner: TrustedXHRAddress) -> JS<XMLHttpRequest> {
let TrustedXHRAddress(addr) = inner;
JS {
ptr: addr as *const XMLHttpRequest
}
}
}
impl JS<Worker> {
pub unsafe fn from_trusted_worker_address(inner: TrustedWorkerAddress) -> JS<Worker> {
let TrustedWorkerAddress(addr) = inner;
JS {
ptr: addr as *const Worker
}
}
}
impl<T: Reflectable> JS<T> { impl<T: Reflectable> JS<T> {
/// Create a new JS-owned value wrapped from a raw Rust pointer. /// Create a new JS-owned value wrapped from a raw Rust pointer.
pub unsafe fn from_raw(raw: *const T) -> JS<T> { pub unsafe fn from_raw(raw: *const T) -> JS<T> {

View file

@ -0,0 +1,178 @@
/* 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/. */
#![deny(missing_docs)]
//! A generic, safe mechnanism by which DOM objects can be pinned and transferred
//! between tasks (or intra-task for asynchronous events). Akin to Gecko's
//! nsMainThreadPtrHandle, this uses thread-safe reference counting and ensures
//! that the actual SpiderMonkey GC integration occurs on the script task via
//! message passing. Ownership of a `Trusted<T>` object means the DOM object of
//! type T to which it points remains alive. Any other behaviour is undefined.
//! To guarantee the lifetime of a DOM object when performing asynchronous operations,
//! obtain a `Trusted<T>` from that object and pass it along with each operation.
//! A usable pointer to the original DOM object can be obtained on the script task
//! from a `Trusted<T>` via the `to_temporary` method.
//!
//! The implementation of Trusted<T> is as follows:
//! A hashtable resides in the script task, keyed on the pointer to the Rust DOM object.
//! The values in this hashtable are atomic reference counts. When a Trusted<T> object is
//! created or cloned, this count is increased. When a Trusted<T> is dropped, the count
//! decreases. If the count hits zero, a message is dispatched to the script task to remove
//! the entry from the hashmap if the count is still zero. The JS reflector for the DOM object
//! is rooted when a hashmap entry is first created, and unrooted when the hashmap entry
//! is removed.
use dom::bindings::js::{Temporary, JS, JSRef};
use dom::bindings::utils::{Reflector, Reflectable};
use script_task::{ScriptMsg, ScriptChan};
use js::jsapi::{JS_AddObjectRoot, JS_RemoveObjectRoot, JSContext};
use libc;
use std::cell::RefCell;
use std::collections::hash_map::{HashMap, Vacant, Occupied};
use std::sync::{Arc, Mutex};
local_data_key!(pub LiveReferences: LiveDOMReferences)
/// A safe wrapper around a raw pointer to a DOM object that can be
/// shared among tasks for use in asynchronous operations. The underlying
/// DOM object is guaranteed to live at least as long as the last outstanding
/// `Trusted<T>` instance.
pub struct Trusted<T> {
/// A pointer to the Rust DOM object of type T, but void to allow
/// sending `Trusted<T>` between tasks, regardless of T's sendability.
ptr: *const libc::c_void,
refcount: Arc<Mutex<uint>>,
script_chan: Box<ScriptChan + Send>,
owner_thread: *const libc::c_void,
}
impl<T: Reflectable> Trusted<T> {
/// Create a new `Trusted<T>` instance from an existing DOM pointer. The DOM object will
/// be prevented from being GCed for the duration of the resulting `Trusted<T>` object's
/// lifetime.
pub fn new(cx: *mut JSContext, ptr: JSRef<T>, script_chan: Box<ScriptChan + Send>) -> Trusted<T> {
let live_references = LiveReferences.get().unwrap();
let refcount = live_references.addref(cx, &*ptr as *const T);
Trusted {
ptr: &*ptr as *const T as *const libc::c_void,
refcount: refcount,
script_chan: script_chan,
owner_thread: (&*live_references) as *const _ as *const libc::c_void,
}
}
/// Obtain a usable DOM pointer from a pinned `Trusted<T>` value. Fails if used on
/// a different thread than the original value from which this `Trusted<T>` was
/// obtained.
pub fn to_temporary(&self) -> Temporary<T> {
assert!({
let live_references = LiveReferences.get().unwrap();
self.owner_thread == (&*live_references) as *const _ as *const libc::c_void
});
unsafe {
Temporary::new(JS::from_raw(self.ptr as *const T))
}
}
}
impl<T: Reflectable> Clone for Trusted<T> {
fn clone(&self) -> Trusted<T> {
{
let mut refcount = self.refcount.lock();
*refcount += 1;
}
Trusted {
ptr: self.ptr,
refcount: self.refcount.clone(),
script_chan: self.script_chan.clone(),
owner_thread: self.owner_thread,
}
}
}
#[unsafe_destructor]
impl<T: Reflectable> Drop for Trusted<T> {
fn drop(&mut self) {
let mut refcount = self.refcount.lock();
assert!(*refcount > 0);
*refcount -= 1;
if *refcount == 0 {
self.script_chan.send(ScriptMsg::RefcountCleanup(self.ptr));
}
}
}
/// The set of live, pinned DOM objects that are currently prevented
/// from being garbage collected due to outstanding references.
pub struct LiveDOMReferences {
// keyed on pointer to Rust DOM object
table: RefCell<HashMap<*const libc::c_void, Arc<Mutex<uint>>>>
}
impl LiveDOMReferences {
/// Set up the task-local data required for storing the outstanding DOM references.
pub fn initialize() {
LiveReferences.replace(Some(LiveDOMReferences {
table: RefCell::new(HashMap::new()),
}));
}
fn addref<T: Reflectable>(&self, cx: *mut JSContext, ptr: *const T) -> Arc<Mutex<uint>> {
let mut table = self.table.borrow_mut();
match table.entry(ptr as *const libc::c_void) {
Occupied(mut entry) => {
let refcount = entry.get_mut();
*refcount.lock() += 1;
refcount.clone()
}
Vacant(entry) => {
unsafe {
let rootable = (*ptr).reflector().rootable();
JS_AddObjectRoot(cx, rootable);
}
let refcount = Arc::new(Mutex::new(1));
entry.set(refcount.clone());
refcount
}
}
}
/// Unpin the given DOM object if its refcount is 0.
pub fn cleanup(cx: *mut JSContext, raw_reflectable: *const libc::c_void) {
let live_references = LiveReferences.get().unwrap();
let reflectable = raw_reflectable as *const Reflector;
let mut table = live_references.table.borrow_mut();
match table.entry(raw_reflectable) {
Occupied(entry) => {
if *entry.get().lock() != 0 {
// there could have been a new reference taken since
// this message was dispatched.
return;
}
unsafe {
JS_RemoveObjectRoot(cx, (*reflectable).rootable());
}
let _ = entry.take();
}
Vacant(_) => {
// there could be a cleanup message dispatched, then a new
// pinned reference obtained and released before the message
// is processed, at which point there would be no matching
// hashtable entry.
info!("attempt to cleanup an unrecognized reflector");
}
}
}
}
impl Drop for LiveDOMReferences {
fn drop(&mut self) {
assert!(self.table.borrow().keys().count() == 0);
}
}

View file

@ -28,8 +28,10 @@
//! a datatype. //! a datatype.
use dom::bindings::js::JS; use dom::bindings::js::JS;
use dom::bindings::refcounted::Trusted;
use dom::bindings::utils::{Reflectable, Reflector, WindowProxyHandler}; use dom::bindings::utils::{Reflectable, Reflector, WindowProxyHandler};
use dom::node::{Node, TrustedNodeAddress}; use dom::node::{Node, TrustedNodeAddress};
use script_task::ScriptChan;
use collections::hash::{Hash, Hasher}; use collections::hash::{Hash, Hasher};
use cssparser::RGBA; use cssparser::RGBA;
@ -203,6 +205,7 @@ no_jsmanaged_fields!(Receiver<T>)
no_jsmanaged_fields!(Rect<T>) no_jsmanaged_fields!(Rect<T>)
no_jsmanaged_fields!(ImageCacheTask, ScriptControlChan) no_jsmanaged_fields!(ImageCacheTask, ScriptControlChan)
no_jsmanaged_fields!(Atom, Namespace, Timer) no_jsmanaged_fields!(Atom, Namespace, Timer)
no_jsmanaged_fields!(Trusted<T>)
no_jsmanaged_fields!(PropertyDeclarationBlock) no_jsmanaged_fields!(PropertyDeclarationBlock)
// These three are interdependent, if you plan to put jsmanaged data // These three are interdependent, if you plan to put jsmanaged data
// in one of these make sure it is propagated properly to containing structs // in one of these make sure it is propagated properly to containing structs
@ -217,6 +220,13 @@ no_jsmanaged_fields!(UntrustedNodeAddress)
no_jsmanaged_fields!(LengthOrPercentageOrAuto) no_jsmanaged_fields!(LengthOrPercentageOrAuto)
no_jsmanaged_fields!(RGBA) no_jsmanaged_fields!(RGBA)
impl JSTraceable for Box<ScriptChan+Send> {
#[inline]
fn trace(&self, _trc: *mut JSTracer) {
// Do nothing
}
}
impl<'a> JSTraceable for &'a str { impl<'a> JSTraceable for &'a str {
#[inline] #[inline]
fn trace(&self, _: *mut JSTracer) { fn trace(&self, _: *mut JSTracer) {

View file

@ -2,6 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::DedicatedWorkerGlobalScopeBinding; use dom::bindings::codegen::Bindings::DedicatedWorkerGlobalScopeBinding;
use dom::bindings::codegen::Bindings::DedicatedWorkerGlobalScopeBinding::DedicatedWorkerGlobalScopeMethods; use dom::bindings::codegen::Bindings::DedicatedWorkerGlobalScopeBinding::DedicatedWorkerGlobalScopeMethods;
use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull; use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull;
@ -11,6 +12,7 @@ use dom::bindings::error::ErrorResult;
use dom::bindings::error::Error::DataClone; use dom::bindings::error::Error::DataClone;
use dom::bindings::global::GlobalRef; use dom::bindings::global::GlobalRef;
use dom::bindings::js::{JSRef, Temporary, RootCollection}; use dom::bindings::js::{JSRef, Temporary, RootCollection};
use dom::bindings::refcounted::LiveDOMReferences;
use dom::bindings::utils::Reflectable; use dom::bindings::utils::Reflectable;
use dom::eventtarget::{EventTarget, EventTargetHelpers, EventTargetTypeId}; use dom::eventtarget::{EventTarget, EventTargetHelpers, EventTargetTypeId};
use dom::messageevent::MessageEvent; use dom::messageevent::MessageEvent;
@ -34,44 +36,93 @@ use std::rc::Rc;
use std::ptr; use std::ptr;
use url::Url; use url::Url;
/// A ScriptChan that can be cloned freely and will silently send a TrustedWorkerAddress with
/// every message. While this SendableWorkerScriptChan is alive, the associated Worker object
/// will remain alive.
#[deriving(Clone)]
#[jstraceable]
pub struct SendableWorkerScriptChan {
sender: Sender<(TrustedWorkerAddress, ScriptMsg)>,
worker: TrustedWorkerAddress,
}
impl ScriptChan for SendableWorkerScriptChan {
fn send(&self, msg: ScriptMsg) {
self.sender.send((self.worker.clone(), msg));
}
fn clone(&self) -> Box<ScriptChan + Send> {
box SendableWorkerScriptChan {
sender: self.sender.clone(),
worker: self.worker.clone(),
}
}
}
/// Set the `worker` field of a related DedicatedWorkerGlobalScope object to a particular
/// value for the duration of this object's lifetime. This ensures that the related Worker
/// object only lives as long as necessary (ie. while events are being executed), while
/// providing a reference that can be cloned freely.
struct AutoWorkerReset<'a> {
workerscope: JSRef<'a, DedicatedWorkerGlobalScope>,
old_worker: Option<TrustedWorkerAddress>,
}
impl<'a> AutoWorkerReset<'a> {
fn new(workerscope: JSRef<'a, DedicatedWorkerGlobalScope>, worker: TrustedWorkerAddress) -> AutoWorkerReset<'a> {
let reset = AutoWorkerReset {
workerscope: workerscope,
old_worker: workerscope.worker.borrow().clone()
};
*workerscope.worker.borrow_mut() = Some(worker);
reset
}
}
#[unsafe_destructor]
impl<'a> Drop for AutoWorkerReset<'a> {
fn drop(&mut self) {
*self.workerscope.worker.borrow_mut() = self.old_worker.clone();
}
}
#[dom_struct] #[dom_struct]
pub struct DedicatedWorkerGlobalScope { pub struct DedicatedWorkerGlobalScope {
workerglobalscope: WorkerGlobalScope, workerglobalscope: WorkerGlobalScope,
receiver: Receiver<ScriptMsg>, receiver: Receiver<(TrustedWorkerAddress, ScriptMsg)>,
own_sender: Sender<(TrustedWorkerAddress, ScriptMsg)>,
worker: DOMRefCell<Option<TrustedWorkerAddress>>,
/// Sender to the parent thread. /// Sender to the parent thread.
parent_sender: ScriptChan, parent_sender: Box<ScriptChan+Send>,
worker: TrustedWorkerAddress,
} }
impl DedicatedWorkerGlobalScope { impl DedicatedWorkerGlobalScope {
fn new_inherited(worker_url: Url, fn new_inherited(worker_url: Url,
worker: TrustedWorkerAddress,
cx: Rc<Cx>, cx: Rc<Cx>,
resource_task: ResourceTask, resource_task: ResourceTask,
parent_sender: ScriptChan, parent_sender: Box<ScriptChan+Send>,
own_sender: ScriptChan, own_sender: Sender<(TrustedWorkerAddress, ScriptMsg)>,
receiver: Receiver<ScriptMsg>) receiver: Receiver<(TrustedWorkerAddress, ScriptMsg)>)
-> DedicatedWorkerGlobalScope { -> DedicatedWorkerGlobalScope {
DedicatedWorkerGlobalScope { DedicatedWorkerGlobalScope {
workerglobalscope: WorkerGlobalScope::new_inherited( workerglobalscope: WorkerGlobalScope::new_inherited(
WorkerGlobalScopeTypeId::DedicatedGlobalScope, worker_url, cx, WorkerGlobalScopeTypeId::DedicatedGlobalScope, worker_url, cx, resource_task),
resource_task, own_sender),
receiver: receiver, receiver: receiver,
own_sender: own_sender,
parent_sender: parent_sender, parent_sender: parent_sender,
worker: worker, worker: DOMRefCell::new(None),
} }
} }
pub fn new(worker_url: Url, pub fn new(worker_url: Url,
worker: TrustedWorkerAddress,
cx: Rc<Cx>, cx: Rc<Cx>,
resource_task: ResourceTask, resource_task: ResourceTask,
parent_sender: ScriptChan, parent_sender: Box<ScriptChan+Send>,
own_sender: ScriptChan, own_sender: Sender<(TrustedWorkerAddress, ScriptMsg)>,
receiver: Receiver<ScriptMsg>) receiver: Receiver<(TrustedWorkerAddress, ScriptMsg)>)
-> Temporary<DedicatedWorkerGlobalScope> { -> Temporary<DedicatedWorkerGlobalScope> {
let scope = box DedicatedWorkerGlobalScope::new_inherited( let scope = box DedicatedWorkerGlobalScope::new_inherited(
worker_url, worker, cx.clone(), resource_task, parent_sender, worker_url, cx.clone(), resource_task, parent_sender,
own_sender, receiver); own_sender, receiver);
DedicatedWorkerGlobalScopeBinding::Wrap(cx.ptr, scope) DedicatedWorkerGlobalScopeBinding::Wrap(cx.ptr, scope)
} }
@ -81,9 +132,9 @@ impl DedicatedWorkerGlobalScope {
pub fn run_worker_scope(worker_url: Url, pub fn run_worker_scope(worker_url: Url,
worker: TrustedWorkerAddress, worker: TrustedWorkerAddress,
resource_task: ResourceTask, resource_task: ResourceTask,
parent_sender: ScriptChan, parent_sender: Box<ScriptChan+Send>,
own_sender: ScriptChan, own_sender: Sender<(TrustedWorkerAddress, ScriptMsg)>,
receiver: Receiver<ScriptMsg>) { receiver: Receiver<(TrustedWorkerAddress, ScriptMsg)>) {
spawn_named_native(format!("WebWorker for {}", worker_url.serialize()), proc() { spawn_named_native(format!("WebWorker for {}", worker_url.serialize()), proc() {
task_state::initialize(SCRIPT | IN_WORKER); task_state::initialize(SCRIPT | IN_WORKER);
@ -103,46 +154,25 @@ impl DedicatedWorkerGlobalScope {
let (_js_runtime, js_context) = ScriptTask::new_rt_and_cx(); let (_js_runtime, js_context) = ScriptTask::new_rt_and_cx();
let global = DedicatedWorkerGlobalScope::new( let global = DedicatedWorkerGlobalScope::new(
worker_url, worker, js_context.clone(), resource_task, worker_url, js_context.clone(), resource_task,
parent_sender, own_sender, receiver).root(); parent_sender, own_sender, receiver).root();
match js_context.evaluate_script(
global.reflector().get_jsobject(), source, url.serialize(), 1) {
Ok(_) => (),
Err(_) => println!("evaluate_script failed")
}
global.delayed_release_worker();
let scope: JSRef<WorkerGlobalScope> = {
WorkerGlobalScopeCast::from_ref(*global); let _ar = AutoWorkerReset::new(*global, worker);
let target: JSRef<EventTarget> =
EventTargetCast::from_ref(*global); match js_context.evaluate_script(
global.reflector().get_jsobject(), source, url.serialize(), 1) {
Ok(_) => (),
Err(_) => println!("evaluate_script failed")
}
}
loop { loop {
match global.receiver.recv_opt() { match global.receiver.recv_opt() {
Ok(ScriptMsg::DOMMessage(data, nbytes)) => { Ok((linked_worker, msg)) => {
let mut message = UndefinedValue(); let _ar = AutoWorkerReset::new(*global, linked_worker);
unsafe { global.handle_event(msg);
assert!(JS_ReadStructuredClone(
js_context.ptr, data as *const u64, nbytes,
JS_STRUCTURED_CLONE_VERSION, &mut message,
ptr::null(), ptr::null_mut()) != 0);
}
MessageEvent::dispatch_jsval(target, GlobalRef::Worker(scope), message);
global.delayed_release_worker();
},
Ok(ScriptMsg::RunnableMsg(runnable)) => {
runnable.handler()
},
Ok(ScriptMsg::WorkerPostMessage(addr, data, nbytes)) => {
Worker::handle_message(addr, data, nbytes);
},
Ok(ScriptMsg::WorkerRelease(addr)) => {
Worker::handle_release(addr)
},
Ok(ScriptMsg::FireTimer(TimerSource::FromWorker, timer_id)) => {
scope.handle_fire_timer(timer_id);
} }
Ok(_) => panic!("Unexpected message"),
Err(_) => break, Err(_) => break,
} }
} }
@ -150,6 +180,58 @@ impl DedicatedWorkerGlobalScope {
} }
} }
pub trait DedicatedWorkerGlobalScopeHelpers {
fn script_chan(self) -> Box<ScriptChan+Send>;
}
impl<'a> DedicatedWorkerGlobalScopeHelpers for JSRef<'a, DedicatedWorkerGlobalScope> {
fn script_chan(self) -> Box<ScriptChan+Send> {
box SendableWorkerScriptChan {
sender: self.own_sender.clone(),
worker: self.worker.borrow().as_ref().unwrap().clone(),
}
}
}
trait PrivateDedicatedWorkerGlobalScopeHelpers {
fn handle_event(self, msg: ScriptMsg);
}
impl<'a> PrivateDedicatedWorkerGlobalScopeHelpers for JSRef<'a, DedicatedWorkerGlobalScope> {
fn handle_event(self, msg: ScriptMsg) {
match msg {
ScriptMsg::DOMMessage(data, nbytes) => {
let mut message = UndefinedValue();
let scope: JSRef<WorkerGlobalScope> = WorkerGlobalScopeCast::from_ref(self);
unsafe {
assert!(JS_ReadStructuredClone(
scope.get_cx(), data as *const u64, nbytes,
JS_STRUCTURED_CLONE_VERSION, &mut message,
ptr::null(), ptr::null_mut()) != 0);
}
let target: JSRef<EventTarget> = EventTargetCast::from_ref(self);
MessageEvent::dispatch_jsval(target, GlobalRef::Worker(scope), message);
},
ScriptMsg::RunnableMsg(runnable) => {
runnable.handler()
},
ScriptMsg::WorkerPostMessage(addr, data, nbytes) => {
Worker::handle_message(addr, data, nbytes);
},
ScriptMsg::RefcountCleanup(addr) => {
let scope: JSRef<WorkerGlobalScope> = WorkerGlobalScopeCast::from_ref(self);
LiveDOMReferences::cleanup(scope.get_cx(), addr);
}
ScriptMsg::FireTimer(TimerSource::FromWorker, timer_id) => {
let scope: JSRef<WorkerGlobalScope> = WorkerGlobalScopeCast::from_ref(self);
scope.handle_fire_timer(timer_id);
}
_ => panic!("Unexpected message"),
}
}
}
impl<'a> DedicatedWorkerGlobalScopeMethods for JSRef<'a, DedicatedWorkerGlobalScope> { impl<'a> DedicatedWorkerGlobalScopeMethods for JSRef<'a, DedicatedWorkerGlobalScope> {
fn PostMessage(self, cx: *mut JSContext, message: JSVal) -> ErrorResult { fn PostMessage(self, cx: *mut JSContext, message: JSVal) -> ErrorResult {
let mut data = ptr::null_mut(); let mut data = ptr::null_mut();
@ -163,25 +245,14 @@ impl<'a> DedicatedWorkerGlobalScopeMethods for JSRef<'a, DedicatedWorkerGlobalSc
return Err(DataClone); return Err(DataClone);
} }
let ScriptChan(ref sender) = self.parent_sender; let worker = self.worker.borrow().as_ref().unwrap().clone();
sender.send(ScriptMsg::WorkerPostMessage(self.worker, data, nbytes)); self.parent_sender.send(ScriptMsg::WorkerPostMessage(worker, data, nbytes));
Ok(()) Ok(())
} }
event_handler!(message, GetOnmessage, SetOnmessage) event_handler!(message, GetOnmessage, SetOnmessage)
} }
trait PrivateDedicatedWorkerGlobalScopeHelpers {
fn delayed_release_worker(self);
}
impl<'a> PrivateDedicatedWorkerGlobalScopeHelpers for JSRef<'a, DedicatedWorkerGlobalScope> {
fn delayed_release_worker(self) {
let ScriptChan(ref sender) = self.parent_sender;
sender.send(ScriptMsg::WorkerRelease(self.worker));
}
}
impl DedicatedWorkerGlobalScopeDerived for EventTarget { impl DedicatedWorkerGlobalScopeDerived for EventTarget {
fn is_dedicatedworkerglobalscope(&self) -> bool { fn is_dedicatedworkerglobalscope(&self) -> bool {
match *self.type_id() { match *self.type_id() {

View file

@ -204,8 +204,7 @@ impl<'a> HTMLFormElementHelpers for JSRef<'a, HTMLFormElement> {
} }
// This is wrong. https://html.spec.whatwg.org/multipage/forms.html#planned-navigation // This is wrong. https://html.spec.whatwg.org/multipage/forms.html#planned-navigation
let ScriptChan(ref script_chan) = *win.script_chan(); win.script_chan().send(ScriptMsg::TriggerLoad(win.page().id, load_data));
script_chan.send(ScriptMsg::TriggerLoad(win.page().id, load_data));
} }
fn get_form_dataset<'b>(self, submitter: Option<FormSubmitter<'b>>) -> Vec<FormDatum> { fn get_form_dataset<'b>(self, submitter: Option<FormSubmitter<'b>>) -> Vec<FormDatum> {

View file

@ -52,7 +52,7 @@ use time;
#[dom_struct] #[dom_struct]
pub struct Window { pub struct Window {
eventtarget: EventTarget, eventtarget: EventTarget,
script_chan: ScriptChan, script_chan: Box<ScriptChan+Send>,
control_chan: ScriptControlChan, control_chan: ScriptControlChan,
console: MutNullableJS<Console>, console: MutNullableJS<Console>,
location: MutNullableJS<Location>, location: MutNullableJS<Location>,
@ -75,8 +75,8 @@ impl Window {
(*js_info.as_ref().unwrap().js_context).ptr (*js_info.as_ref().unwrap().js_context).ptr
} }
pub fn script_chan<'a>(&'a self) -> &'a ScriptChan { pub fn script_chan(&self) -> Box<ScriptChan+Send> {
&self.script_chan self.script_chan.clone()
} }
pub fn control_chan<'a>(&'a self) -> &'a ScriptControlChan { pub fn control_chan<'a>(&'a self) -> &'a ScriptControlChan {
@ -189,8 +189,7 @@ impl<'a> WindowMethods for JSRef<'a, Window> {
} }
fn Close(self) { fn Close(self) {
let ScriptChan(ref chan) = self.script_chan; self.script_chan.send(ScriptMsg::ExitWindow(self.page.id.clone()));
chan.send(ScriptMsg::ExitWindow(self.page.id.clone()));
} }
fn Document(self) -> Temporary<Document> { fn Document(self) -> Temporary<Document> {
@ -342,11 +341,10 @@ impl<'a> WindowHelpers for JSRef<'a, Window> {
let url = UrlParser::new().base_url(&base_url).parse(href.as_slice()); let url = UrlParser::new().base_url(&base_url).parse(href.as_slice());
// FIXME: handle URL parse errors more gracefully. // FIXME: handle URL parse errors more gracefully.
let url = url.unwrap(); let url = url.unwrap();
let ScriptChan(ref script_chan) = self.script_chan;
if href.as_slice().starts_with("#") { if href.as_slice().starts_with("#") {
script_chan.send(ScriptMsg::TriggerFragment(self.page.id, url)); self.script_chan.send(ScriptMsg::TriggerFragment(self.page.id, url));
} else { } else {
script_chan.send(ScriptMsg::TriggerLoad(self.page.id, LoadData::new(url))); self.script_chan.send(ScriptMsg::TriggerLoad(self.page.id, LoadData::new(url)));
} }
} }
@ -359,7 +357,7 @@ impl<'a> WindowHelpers for JSRef<'a, Window> {
impl Window { impl Window {
pub fn new(cx: *mut JSContext, pub fn new(cx: *mut JSContext,
page: Rc<Page>, page: Rc<Page>,
script_chan: ScriptChan, script_chan: Box<ScriptChan+Send>,
control_chan: ScriptControlChan, control_chan: ScriptControlChan,
compositor: Box<ScriptListener+'static>, compositor: Box<ScriptListener+'static>,
image_cache_task: ImageCacheTask) image_cache_task: ImageCacheTask)

View file

@ -9,7 +9,8 @@ use dom::bindings::codegen::InheritTypes::EventTargetCast;
use dom::bindings::error::{Fallible, ErrorResult}; use dom::bindings::error::{Fallible, ErrorResult};
use dom::bindings::error::Error::{Syntax, DataClone}; use dom::bindings::error::Error::{Syntax, DataClone};
use dom::bindings::global::{GlobalRef, GlobalField}; use dom::bindings::global::{GlobalRef, GlobalField};
use dom::bindings::js::{JS, JSRef, Temporary}; use dom::bindings::js::{JSRef, Temporary};
use dom::bindings::refcounted::Trusted;
use dom::bindings::trace::JSTraceable; use dom::bindings::trace::JSTraceable;
use dom::bindings::utils::{Reflectable, reflect_dom_object}; use dom::bindings::utils::{Reflectable, reflect_dom_object};
use dom::dedicatedworkerglobalscope::DedicatedWorkerGlobalScope; use dom::dedicatedworkerglobalscope::DedicatedWorkerGlobalScope;
@ -20,17 +21,16 @@ use script_task::{ScriptChan, ScriptMsg};
use servo_util::str::DOMString; use servo_util::str::DOMString;
use js::glue::JS_STRUCTURED_CLONE_VERSION; use js::glue::JS_STRUCTURED_CLONE_VERSION;
use js::jsapi::{JSContext, JS_AddObjectRoot, JS_RemoveObjectRoot, JSTracer}; use js::jsapi::JSContext;
use js::jsapi::{JS_ReadStructuredClone, JS_WriteStructuredClone, JS_ClearPendingException}; use js::jsapi::{JS_ReadStructuredClone, JS_WriteStructuredClone, JS_ClearPendingException};
use js::jsval::{JSVal, UndefinedValue}; use js::jsval::{JSVal, UndefinedValue};
use url::UrlParser; use url::UrlParser;
use libc::{c_void, size_t}; use libc::size_t;
use std::cell::Cell; use std::cell::Cell;
use std::ptr; use std::ptr;
pub struct TrustedWorkerAddress(pub *const c_void); pub type TrustedWorkerAddress = Trusted<Worker>;
no_jsmanaged_fields!(TrustedWorkerAddress)
#[dom_struct] #[dom_struct]
pub struct Worker { pub struct Worker {
@ -39,11 +39,11 @@ pub struct Worker {
global: GlobalField, global: GlobalField,
/// Sender to the Receiver associated with the DedicatedWorkerGlobalScope /// Sender to the Receiver associated with the DedicatedWorkerGlobalScope
/// this Worker created. /// this Worker created.
sender: ScriptChan, sender: Sender<(TrustedWorkerAddress, ScriptMsg)>,
} }
impl Worker { impl Worker {
fn new_inherited(global: &GlobalRef, sender: ScriptChan) -> Worker { fn new_inherited(global: &GlobalRef, sender: Sender<(TrustedWorkerAddress, ScriptMsg)>) -> Worker {
Worker { Worker {
eventtarget: EventTarget::new_inherited(EventTargetTypeId::Worker), eventtarget: EventTarget::new_inherited(EventTargetTypeId::Worker),
refcount: Cell::new(0), refcount: Cell::new(0),
@ -52,7 +52,7 @@ impl Worker {
} }
} }
pub fn new(global: &GlobalRef, sender: ScriptChan) -> Temporary<Worker> { pub fn new(global: &GlobalRef, sender: Sender<(TrustedWorkerAddress, ScriptMsg)>) -> Temporary<Worker> {
reflect_dom_object(box Worker::new_inherited(global, sender), reflect_dom_object(box Worker::new_inherited(global, sender),
*global, *global,
WorkerBinding::Wrap) WorkerBinding::Wrap)
@ -68,13 +68,13 @@ impl Worker {
}; };
let resource_task = global.resource_task(); let resource_task = global.resource_task();
let (receiver, sender) = ScriptChan::new();
let (sender, receiver) = channel();
let worker = Worker::new(global, sender.clone()).root(); let worker = Worker::new(global, sender.clone()).root();
let worker_ref = worker.addref(); let worker_ref = Trusted::new(global.get_cx(), *worker, global.script_chan());
DedicatedWorkerGlobalScope::run_worker_scope( DedicatedWorkerGlobalScope::run_worker_scope(
worker_url, worker_ref, resource_task, global.script_chan().clone(), worker_url, worker_ref, resource_task, global.script_chan(),
sender, receiver); sender, receiver);
Ok(Temporary::from_rooted(*worker)) Ok(Temporary::from_rooted(*worker))
@ -82,7 +82,7 @@ impl Worker {
pub fn handle_message(address: TrustedWorkerAddress, pub fn handle_message(address: TrustedWorkerAddress,
data: *mut u64, nbytes: size_t) { data: *mut u64, nbytes: size_t) {
let worker = unsafe { JS::from_trusted_worker_address(address).root() }; let worker = address.to_temporary().root();
let global = worker.global.root(); let global = worker.global.root();
@ -99,38 +99,6 @@ impl Worker {
} }
} }
impl Worker {
// Creates a trusted address to the object, and roots it. Always pair this with a release()
pub fn addref(&self) -> TrustedWorkerAddress {
let refcount = self.refcount.get();
if refcount == 0 {
let cx = self.global.root().root_ref().get_cx();
unsafe {
JS_AddObjectRoot(cx, self.reflector().rootable());
}
}
self.refcount.set(refcount + 1);
TrustedWorkerAddress(self as *const Worker as *const c_void)
}
pub fn release(&self) {
let refcount = self.refcount.get();
assert!(refcount > 0)
self.refcount.set(refcount - 1);
if refcount == 1 {
let cx = self.global.root().root_ref().get_cx();
unsafe {
JS_RemoveObjectRoot(cx, self.reflector().rootable());
}
}
}
pub fn handle_release(address: TrustedWorkerAddress) {
let worker = unsafe { JS::from_trusted_worker_address(address).root() };
worker.release();
}
}
impl<'a> WorkerMethods for JSRef<'a, Worker> { impl<'a> WorkerMethods for JSRef<'a, Worker> {
fn PostMessage(self, cx: *mut JSContext, message: JSVal) -> ErrorResult { fn PostMessage(self, cx: *mut JSContext, message: JSVal) -> ErrorResult {
let mut data = ptr::null_mut(); let mut data = ptr::null_mut();
@ -144,9 +112,8 @@ impl<'a> WorkerMethods for JSRef<'a, Worker> {
return Err(DataClone); return Err(DataClone);
} }
self.addref(); let address = Trusted::new(cx, self, self.global.root().root_ref().script_chan().clone());
let ScriptChan(ref sender) = self.sender; self.sender.send((address, ScriptMsg::DOMMessage(data, nbytes)));
sender.send(ScriptMsg::DOMMessage(data, nbytes));
Ok(()) Ok(())
} }

View file

@ -2,14 +2,16 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use dom::bindings::codegen::Bindings::WorkerGlobalScopeBinding::WorkerGlobalScopeMethods;
use dom::bindings::codegen::Bindings::FunctionBinding::Function; use dom::bindings::codegen::Bindings::FunctionBinding::Function;
use dom::bindings::codegen::Bindings::WorkerGlobalScopeBinding::WorkerGlobalScopeMethods;
use dom::bindings::codegen::InheritTypes::DedicatedWorkerGlobalScopeCast;
use dom::bindings::error::{ErrorResult, Fallible}; use dom::bindings::error::{ErrorResult, Fallible};
use dom::bindings::error::Error::{Syntax, Network, FailureUnknown}; use dom::bindings::error::Error::{Syntax, Network, FailureUnknown};
use dom::bindings::global::GlobalRef; use dom::bindings::global::GlobalRef;
use dom::bindings::js::{MutNullableJS, JSRef, Temporary}; use dom::bindings::js::{MutNullableJS, JSRef, Temporary};
use dom::bindings::utils::Reflectable; use dom::bindings::utils::Reflectable;
use dom::console::Console; use dom::console::Console;
use dom::dedicatedworkerglobalscope::{DedicatedWorkerGlobalScope, DedicatedWorkerGlobalScopeHelpers};
use dom::eventtarget::{EventTarget, EventTargetTypeId}; use dom::eventtarget::{EventTarget, EventTargetTypeId};
use dom::workerlocation::WorkerLocation; use dom::workerlocation::WorkerLocation;
use dom::workernavigator::WorkerNavigator; use dom::workernavigator::WorkerNavigator;
@ -40,7 +42,6 @@ pub struct WorkerGlobalScope {
worker_url: Url, worker_url: Url,
js_context: Rc<Cx>, js_context: Rc<Cx>,
resource_task: ResourceTask, resource_task: ResourceTask,
script_chan: ScriptChan,
location: MutNullableJS<WorkerLocation>, location: MutNullableJS<WorkerLocation>,
navigator: MutNullableJS<WorkerNavigator>, navigator: MutNullableJS<WorkerNavigator>,
console: MutNullableJS<Console>, console: MutNullableJS<Console>,
@ -51,18 +52,16 @@ impl WorkerGlobalScope {
pub fn new_inherited(type_id: WorkerGlobalScopeTypeId, pub fn new_inherited(type_id: WorkerGlobalScopeTypeId,
worker_url: Url, worker_url: Url,
cx: Rc<Cx>, cx: Rc<Cx>,
resource_task: ResourceTask, resource_task: ResourceTask) -> WorkerGlobalScope {
script_chan: ScriptChan) -> WorkerGlobalScope {
WorkerGlobalScope { WorkerGlobalScope {
eventtarget: EventTarget::new_inherited(EventTargetTypeId::WorkerGlobalScope(type_id)), eventtarget: EventTarget::new_inherited(EventTargetTypeId::WorkerGlobalScope(type_id)),
worker_url: worker_url, worker_url: worker_url,
js_context: cx, js_context: cx,
resource_task: resource_task, resource_task: resource_task,
script_chan: script_chan,
location: Default::default(), location: Default::default(),
navigator: Default::default(), navigator: Default::default(),
console: Default::default(), console: Default::default(),
timers: TimerManager::new() timers: TimerManager::new(),
} }
} }
@ -82,10 +81,6 @@ impl WorkerGlobalScope {
pub fn get_url<'a>(&'a self) -> &'a Url { pub fn get_url<'a>(&'a self) -> &'a Url {
&self.worker_url &self.worker_url
} }
pub fn script_chan<'a>(&'a self) -> &'a ScriptChan {
&self.script_chan
}
} }
impl<'a> WorkerGlobalScopeMethods for JSRef<'a, WorkerGlobalScope> { impl<'a> WorkerGlobalScopeMethods for JSRef<'a, WorkerGlobalScope> {
@ -153,7 +148,7 @@ impl<'a> WorkerGlobalScopeMethods for JSRef<'a, WorkerGlobalScope> {
timeout, timeout,
IsInterval::NonInterval, IsInterval::NonInterval,
TimerSource::FromWorker, TimerSource::FromWorker,
self.script_chan.clone()) self.script_chan())
} }
fn ClearTimeout(self, handle: i32) { fn ClearTimeout(self, handle: i32) {
@ -166,7 +161,7 @@ impl<'a> WorkerGlobalScopeMethods for JSRef<'a, WorkerGlobalScope> {
timeout, timeout,
IsInterval::Interval, IsInterval::Interval,
TimerSource::FromWorker, TimerSource::FromWorker,
self.script_chan.clone()) self.script_chan())
} }
fn ClearInterval(self, handle: i32) { fn ClearInterval(self, handle: i32) {
@ -176,13 +171,26 @@ impl<'a> WorkerGlobalScopeMethods for JSRef<'a, WorkerGlobalScope> {
pub trait WorkerGlobalScopeHelpers { pub trait WorkerGlobalScopeHelpers {
fn handle_fire_timer(self, timer_id: TimerId); fn handle_fire_timer(self, timer_id: TimerId);
fn script_chan(self) -> Box<ScriptChan+Send>;
fn get_cx(self) -> *mut JSContext;
} }
impl<'a> WorkerGlobalScopeHelpers for JSRef<'a, WorkerGlobalScope> { impl<'a> WorkerGlobalScopeHelpers for JSRef<'a, WorkerGlobalScope> {
fn script_chan(self) -> Box<ScriptChan+Send> {
let dedicated: Option<JSRef<DedicatedWorkerGlobalScope>> =
DedicatedWorkerGlobalScopeCast::to_ref(self);
match dedicated {
Some(dedicated) => dedicated.script_chan(),
None => panic!("need to implement a sender for SharedWorker"),
}
}
fn handle_fire_timer(self, timer_id: TimerId) { fn handle_fire_timer(self, timer_id: TimerId) {
self.timers.fire_timer(timer_id, self); self.timers.fire_timer(timer_id, self);
} }
fn get_cx(self) -> *mut JSContext {
self.js_context.ptr
}
} }

View file

@ -15,6 +15,7 @@ use dom::bindings::error::Error::{InvalidState, InvalidAccess};
use dom::bindings::error::Error::{Network, Syntax, Security, Abort, Timeout}; use dom::bindings::error::Error::{Network, Syntax, Security, Abort, Timeout};
use dom::bindings::global::{GlobalField, GlobalRef, GlobalRoot}; use dom::bindings::global::{GlobalField, GlobalRef, GlobalRoot};
use dom::bindings::js::{MutNullableJS, JS, JSRef, Temporary, OptionalRootedRootable}; use dom::bindings::js::{MutNullableJS, JS, JSRef, Temporary, OptionalRootedRootable};
use dom::bindings::refcounted::Trusted;
use dom::bindings::str::ByteString; use dom::bindings::str::ByteString;
use dom::bindings::utils::{Reflectable, reflect_dom_object}; use dom::bindings::utils::{Reflectable, reflect_dom_object};
use dom::document::Document; use dom::document::Document;
@ -37,13 +38,10 @@ use hyper::http::RawStatus;
use hyper::mime::{mod, Mime}; use hyper::mime::{mod, Mime};
use hyper::method::{Method, Get, Head, Connect, Trace, Extension}; use hyper::method::{Method, Get, Head, Connect, Trace, Extension};
use js::jsapi::{JS_AddObjectRoot, JS_ParseJSON, JS_RemoveObjectRoot, JSContext}; use js::jsapi::{JS_ParseJSON, JSContext};
use js::jsapi::JS_ClearPendingException; use js::jsapi::JS_ClearPendingException;
use js::jsval::{JSVal, NullValue, UndefinedValue}; use js::jsval::{JSVal, NullValue, UndefinedValue};
use libc;
use libc::c_void;
use net::resource_task::{ResourceTask, ResourceCORSData, Load, LoadData, LoadResponse, Payload, Done}; use net::resource_task::{ResourceTask, ResourceCORSData, Load, LoadData, LoadResponse, Payload, Done};
use cors::{allow_cross_origin_request, CORSRequest, RequestMode}; use cors::{allow_cross_origin_request, CORSRequest, RequestMode};
use servo_util::str::DOMString; use servo_util::str::DOMString;
@ -73,15 +71,6 @@ enum XMLHttpRequestState {
XHRDone = 4, // So as not to conflict with the ProgressMsg `Done` XHRDone = 4, // So as not to conflict with the ProgressMsg `Done`
} }
struct XHRReleaseHandler(TrustedXHRAddress);
impl Runnable for XHRReleaseHandler {
fn handler(&self) {
let XHRReleaseHandler(addr) = *self;
XMLHttpRequest::handle_release(addr);
}
}
struct XHRProgressHandler { struct XHRProgressHandler {
addr: TrustedXHRAddress, addr: TrustedXHRAddress,
progress: XHRProgress, progress: XHRProgress,
@ -95,7 +84,7 @@ impl XHRProgressHandler {
impl Runnable for XHRProgressHandler { impl Runnable for XHRProgressHandler {
fn handler(&self) { fn handler(&self) {
XMLHttpRequest::handle_progress(self.addr, self.progress.clone()); XMLHttpRequest::handle_progress(self.addr.clone(), self.progress.clone());
} }
} }
@ -128,7 +117,7 @@ impl XHRProgress {
enum SyncOrAsync<'a> { enum SyncOrAsync<'a> {
Sync(JSRef<'a, XMLHttpRequest>), Sync(JSRef<'a, XMLHttpRequest>),
Async(TrustedXHRAddress, &'a ScriptChan) Async(TrustedXHRAddress, Box<ScriptChan+Send>)
} }
enum TerminateReason { enum TerminateReason {
@ -162,7 +151,6 @@ pub struct XMLHttpRequest {
send_flag: Cell<bool>, send_flag: Cell<bool>,
global: GlobalField, global: GlobalField,
pinned_count: Cell<uint>,
timer: DOMRefCell<Timer>, timer: DOMRefCell<Timer>,
fetch_time: Cell<i64>, fetch_time: Cell<i64>,
terminate_sender: DOMRefCell<Option<Sender<TerminateReason>>>, terminate_sender: DOMRefCell<Option<Sender<TerminateReason>>>,
@ -196,7 +184,6 @@ impl XMLHttpRequest {
upload_events: Cell::new(false), upload_events: Cell::new(false),
global: GlobalField::from_rooted(global), global: GlobalField::from_rooted(global),
pinned_count: Cell::new(0),
timer: DOMRefCell::new(Timer::new().unwrap()), timer: DOMRefCell::new(Timer::new().unwrap()),
fetch_time: Cell::new(0), fetch_time: Cell::new(0),
terminate_sender: DOMRefCell::new(None), terminate_sender: DOMRefCell::new(None),
@ -213,14 +200,8 @@ impl XMLHttpRequest {
} }
pub fn handle_progress(addr: TrustedXHRAddress, progress: XHRProgress) { pub fn handle_progress(addr: TrustedXHRAddress, progress: XHRProgress) {
unsafe { let xhr = addr.to_temporary().root();
let xhr = JS::from_trusted_xhr_address(addr).root(); xhr.process_partial_response(progress);
xhr.process_partial_response(progress);
}
}
pub fn handle_release(addr: TrustedXHRAddress) {
addr.release_once();
} }
fn fetch(fetch_type: &SyncOrAsync, resource_task: ResourceTask, fn fetch(fetch_type: &SyncOrAsync, resource_task: ResourceTask,
@ -233,9 +214,8 @@ impl XMLHttpRequest {
SyncOrAsync::Sync(xhr) => { SyncOrAsync::Sync(xhr) => {
xhr.process_partial_response(msg); xhr.process_partial_response(msg);
}, },
SyncOrAsync::Async(addr, script_chan) => { SyncOrAsync::Async(ref addr, ref script_chan) => {
let ScriptChan(ref chan) = *script_chan; script_chan.send(ScriptMsg::RunnableMsg(box XHRProgressHandler::new(addr.clone(), msg)));
chan.send(ScriptMsg::RunnableMsg(box XHRProgressHandler::new(addr, msg)));
} }
} }
} }
@ -633,25 +613,20 @@ impl<'a> XMLHttpRequestMethods for JSRef<'a, XMLHttpRequest> {
terminate_receiver, cors_request, gen_id, start_port); terminate_receiver, cors_request, gen_id, start_port);
} else { } else {
self.fetch_time.set(time::now().to_timespec().sec); self.fetch_time.set(time::now().to_timespec().sec);
let script_chan = global.root_ref().script_chan().clone(); let script_chan = global.root_ref().script_chan();
// Pin the object before launching the fetch task. // Pin the object before launching the fetch task. This is to ensure that
// The `ScriptMsg::RunnableMsg` sent when the fetch task completes will // the object will stay alive as long as there are (possibly cancelled)
// unpin it. This is to ensure that the object will stay alive // inflight events queued up in the script task's port.
// as long as there are (possibly cancelled) inflight events queued up let addr = Trusted::new(self.global.root().root_ref().get_cx(), self,
// in the script task's port script_chan.clone());
let addr = unsafe {
self.to_trusted()
};
spawn_named("XHRTask", proc() { spawn_named("XHRTask", proc() {
let _ = XMLHttpRequest::fetch(&mut SyncOrAsync::Async(addr, &script_chan), let _ = XMLHttpRequest::fetch(&mut SyncOrAsync::Async(addr, script_chan),
resource_task, resource_task,
load_data, load_data,
terminate_receiver, terminate_receiver,
cors_request, cors_request,
gen_id, gen_id,
start_port); start_port);
let ScriptChan(ref chan) = script_chan;
chan.send(ScriptMsg::RunnableMsg(box XHRReleaseHandler(addr)));
}); });
let timeout = self.timeout.get(); let timeout = self.timeout.get();
if timeout > 0 { if timeout > 0 {
@ -768,20 +743,9 @@ impl XMLHttpRequestDerived for EventTarget {
} }
} }
pub struct TrustedXHRAddress(pub *const c_void); pub type TrustedXHRAddress = Trusted<XMLHttpRequest>;
impl TrustedXHRAddress {
pub fn release_once(self) {
unsafe {
JS::from_trusted_xhr_address(self).root().release_once();
}
}
}
trait PrivateXMLHttpRequestHelpers { trait PrivateXMLHttpRequestHelpers {
unsafe fn to_trusted(self) -> TrustedXHRAddress;
fn release_once(self);
fn change_ready_state(self, XMLHttpRequestState); fn change_ready_state(self, XMLHttpRequestState);
fn process_partial_response(self, progress: XHRProgress); fn process_partial_response(self, progress: XHRProgress);
fn terminate_ongoing_fetch(self); fn terminate_ongoing_fetch(self);
@ -796,33 +760,6 @@ trait PrivateXMLHttpRequestHelpers {
} }
impl<'a> PrivateXMLHttpRequestHelpers for JSRef<'a, XMLHttpRequest> { impl<'a> PrivateXMLHttpRequestHelpers for JSRef<'a, XMLHttpRequest> {
// Creates a trusted address to the object, and roots it. Always pair this with a release()
unsafe fn to_trusted(self) -> TrustedXHRAddress {
if self.pinned_count.get() == 0 {
JS_AddObjectRoot(self.global.root().root_ref().get_cx(), self.reflector().rootable());
}
let pinned_count = self.pinned_count.get();
self.pinned_count.set(pinned_count + 1);
TrustedXHRAddress(self.deref() as *const XMLHttpRequest as *const libc::c_void)
}
fn release_once(self) {
if self.sync.get() {
// Lets us call this at various termination cases without having to
// check self.sync every time, since the pinning mechanism only is
// meaningful during an async fetch
return;
}
assert!(self.pinned_count.get() > 0)
let pinned_count = self.pinned_count.get();
self.pinned_count.set(pinned_count - 1);
if self.pinned_count.get() == 0 {
unsafe {
JS_RemoveObjectRoot(self.global.root().root_ref().get_cx(), self.reflector().rootable());
}
}
}
fn change_ready_state(self, rs: XMLHttpRequestState) { fn change_ready_state(self, rs: XMLHttpRequestState) {
assert!(self.ready_state.get() != rs) assert!(self.ready_state.get() != rs)
self.ready_state.set(rs); self.ready_state.set(rs);

View file

@ -59,6 +59,7 @@ pub mod dom {
pub mod cell; pub mod cell;
pub mod global; pub mod global;
pub mod js; pub mod js;
pub mod refcounted;
pub mod utils; pub mod utils;
pub mod callback; pub mod callback;
pub mod error; pub mod error;

View file

@ -15,6 +15,7 @@ use dom::bindings::conversions::FromJSValConvertible;
use dom::bindings::conversions::StringificationBehavior; use dom::bindings::conversions::StringificationBehavior;
use dom::bindings::global::GlobalRef; use dom::bindings::global::GlobalRef;
use dom::bindings::js::{JS, JSRef, RootCollection, Temporary, OptionalRootable}; use dom::bindings::js::{JS, JSRef, RootCollection, Temporary, OptionalRootable};
use dom::bindings::refcounted::LiveDOMReferences;
use dom::bindings::trace::JSTraceable; use dom::bindings::trace::JSTraceable;
use dom::bindings::utils::{wrap_for_same_compartment, pre_wrap}; use dom::bindings::utils::{wrap_for_same_compartment, pre_wrap};
use dom::document::{Document, IsHTMLDocument, DocumentHelpers, DocumentSource}; use dom::document::{Document, IsHTMLDocument, DocumentHelpers, DocumentSource};
@ -63,13 +64,14 @@ use geom::point::Point2D;
use hyper::header::{Header, HeaderFormat}; use hyper::header::{Header, HeaderFormat};
use hyper::header::common::util as header_util; use hyper::header::common::util as header_util;
use js::jsapi::{JS_SetWrapObjectCallbacks, JS_SetGCZeal, JS_DEFAULT_ZEAL_FREQ, JS_GC}; use js::jsapi::{JS_SetWrapObjectCallbacks, JS_SetGCZeal, JS_DEFAULT_ZEAL_FREQ, JS_GC};
use js::jsapi::{JSContext, JSRuntime, JSTracer}; use js::jsapi::{JSContext, JSRuntime};
use js::jsapi::{JS_SetGCParameter, JSGC_MAX_BYTES}; use js::jsapi::{JS_SetGCParameter, JSGC_MAX_BYTES};
use js::jsapi::{JS_SetGCCallback, JSGCStatus, JSGC_BEGIN, JSGC_END}; use js::jsapi::{JS_SetGCCallback, JSGCStatus, JSGC_BEGIN, JSGC_END};
use js::rust::{Cx, RtUtils}; use js::rust::{Cx, RtUtils};
use js; use js;
use url::Url; use url::Url;
use libc;
use libc::size_t; use libc::size_t;
use std::any::{Any, AnyRefExt}; use std::any::{Any, AnyRefExt};
use std::comm::{channel, Sender, Receiver, Select}; use std::comm::{channel, Sender, Receiver, Select};
@ -114,23 +116,41 @@ pub enum ScriptMsg {
DOMMessage(*mut u64, size_t), DOMMessage(*mut u64, size_t),
/// Posts a message to the Worker object (dispatched to all tasks). /// Posts a message to the Worker object (dispatched to all tasks).
WorkerPostMessage(TrustedWorkerAddress, *mut u64, size_t), WorkerPostMessage(TrustedWorkerAddress, *mut u64, size_t),
/// Releases one reference to the Worker object (dispatched to all tasks).
WorkerRelease(TrustedWorkerAddress),
/// Generic message that encapsulates event handling. /// Generic message that encapsulates event handling.
RunnableMsg(Box<Runnable+Send>), RunnableMsg(Box<Runnable+Send>),
/// A DOM object's last pinned reference was removed (dispatched to all tasks).
RefcountCleanup(*const libc::c_void),
}
/// A cloneable interface for communicating with an event loop.
pub trait ScriptChan {
/// Send a message to the associated event loop.
fn send(&self, msg: ScriptMsg);
/// Clone this handle.
fn clone(&self) -> Box<ScriptChan+Send>;
} }
/// Encapsulates internal communication within the script task. /// Encapsulates internal communication within the script task.
#[deriving(Clone)] #[jstraceable]
pub struct ScriptChan(pub Sender<ScriptMsg>); pub struct NonWorkerScriptChan(pub Sender<ScriptMsg>);
no_jsmanaged_fields!(ScriptChan) impl ScriptChan for NonWorkerScriptChan {
fn send(&self, msg: ScriptMsg) {
let NonWorkerScriptChan(ref chan) = *self;
chan.send(msg);
}
impl ScriptChan { fn clone(&self) -> Box<ScriptChan+Send> {
let NonWorkerScriptChan(ref chan) = *self;
box NonWorkerScriptChan(chan.clone())
}
}
impl NonWorkerScriptChan {
/// Creates a new script chan. /// Creates a new script chan.
pub fn new() -> (Receiver<ScriptMsg>, ScriptChan) { pub fn new() -> (Receiver<ScriptMsg>, Box<NonWorkerScriptChan>) {
let (chan, port) = channel(); let (chan, port) = channel();
(port, ScriptChan(chan)) (port, box NonWorkerScriptChan(chan))
} }
} }
@ -165,7 +185,7 @@ pub struct ScriptTask {
port: Receiver<ScriptMsg>, port: Receiver<ScriptMsg>,
/// A channel to hand out to script task-based entities that need to be able to enqueue /// A channel to hand out to script task-based entities that need to be able to enqueue
/// events in the event queue. /// events in the event queue.
chan: ScriptChan, chan: NonWorkerScriptChan,
/// A channel to hand out to tasks that need to respond to a message from the script task. /// A channel to hand out to tasks that need to respond to a message from the script task.
control_chan: ScriptControlChan, control_chan: ScriptControlChan,
@ -280,7 +300,7 @@ impl ScriptTaskFactory for ScriptTask {
box compositor as Box<ScriptListener>, box compositor as Box<ScriptListener>,
layout_chan, layout_chan,
script_port, script_port,
ScriptChan(script_chan), NonWorkerScriptChan(script_chan),
control_chan, control_chan,
control_port, control_port,
constellation_chan, constellation_chan,
@ -312,7 +332,7 @@ impl ScriptTask {
compositor: Box<ScriptListener+'static>, compositor: Box<ScriptListener+'static>,
layout_chan: LayoutChan, layout_chan: LayoutChan,
port: Receiver<ScriptMsg>, port: Receiver<ScriptMsg>,
chan: ScriptChan, chan: NonWorkerScriptChan,
control_chan: ScriptControlChan, control_chan: ScriptControlChan,
control_port: Receiver<ConstellationControlMsg>, control_port: Receiver<ConstellationControlMsg>,
constellation_chan: ConstellationChan, constellation_chan: ConstellationChan,
@ -368,6 +388,7 @@ impl ScriptTask {
} }
pub fn new_rt_and_cx() -> (js::rust::rt, Rc<Cx>) { pub fn new_rt_and_cx() -> (js::rust::rt, Rc<Cx>) {
LiveDOMReferences::initialize();
let js_runtime = js::rust::rt(); let js_runtime = js::rust::rt();
assert!({ assert!({
let ptr: *mut JSRuntime = (*js_runtime).ptr; let ptr: *mut JSRuntime = (*js_runtime).ptr;
@ -577,10 +598,10 @@ impl ScriptTask {
panic!("unexpected message"), panic!("unexpected message"),
ScriptMsg::WorkerPostMessage(addr, data, nbytes) => ScriptMsg::WorkerPostMessage(addr, data, nbytes) =>
Worker::handle_message(addr, data, nbytes), Worker::handle_message(addr, data, nbytes),
ScriptMsg::WorkerRelease(addr) =>
Worker::handle_release(addr),
ScriptMsg::RunnableMsg(runnable) => ScriptMsg::RunnableMsg(runnable) =>
runnable.handler(), runnable.handler(),
ScriptMsg::RefcountCleanup(addr) =>
LiveDOMReferences::cleanup(self.get_cx(), addr),
} }
} }

View file

@ -100,7 +100,7 @@ impl TimerManager {
timeout: i32, timeout: i32,
is_interval: IsInterval, is_interval: IsInterval,
source: TimerSource, source: TimerSource,
script_chan: ScriptChan) script_chan: Box<ScriptChan+Send>)
-> i32 { -> i32 {
let timeout = cmp::max(0, timeout) as u64; let timeout = cmp::max(0, timeout) as u64;
let handle = self.next_timer_handle.get(); let handle = self.next_timer_handle.get();
@ -136,8 +136,7 @@ impl TimerManager {
let id = select.wait(); let id = select.wait();
if id == timeout_handle.id() { if id == timeout_handle.id() {
timeout_port.recv(); timeout_port.recv();
let ScriptChan(ref chan) = script_chan; script_chan.send(ScriptMsg::FireTimer(source, TimerId(handle)));
chan.send(ScriptMsg::FireTimer(source, TimerId(handle)));
if is_interval == IsInterval::NonInterval { if is_interval == IsInterval::NonInterval {
break; break;
} }