aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorbors-servo <metajack+bors@gmail.com>2014-12-29 11:57:45 -0700
committerbors-servo <metajack+bors@gmail.com>2014-12-29 11:57:45 -0700
commit2c259f477c41331e66beab8bda865971982a1ff4 (patch)
tree12b1b56eec1482d6b6e31e85383a309856a39dc7
parentf76a460c53dfddef74262eceaf4b163b551adc08 (diff)
parent9a7cd3113403fe44a8919f049720b67bfa92c9f1 (diff)
downloadservo-2c259f477c41331e66beab8bda865971982a1ff4.tar.gz
servo-2c259f477c41331e66beab8bda865971982a1ff4.zip
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.
-rw-r--r--components/script/dom/bindings/global.rs4
-rw-r--r--components/script/dom/bindings/js.rs20
-rw-r--r--components/script/dom/bindings/refcounted.rs178
-rw-r--r--components/script/dom/bindings/trace.rs10
-rw-r--r--components/script/dom/dedicatedworkerglobalscope.rs201
-rw-r--r--components/script/dom/htmlformelement.rs3
-rw-r--r--components/script/dom/window.rs16
-rw-r--r--components/script/dom/worker.rs61
-rw-r--r--components/script/dom/workerglobalscope.rs32
-rw-r--r--components/script/dom/xmlhttprequest.rs95
-rw-r--r--components/script/lib.rs1
-rw-r--r--components/script/script_task.rs49
-rw-r--r--components/script/timers.rs5
13 files changed, 422 insertions, 253 deletions
diff --git a/components/script/dom/bindings/global.rs b/components/script/dom/bindings/global.rs
index 4efeb7aaf67..38e018fe24f 100644
--- a/components/script/dom/bindings/global.rs
+++ b/components/script/dom/bindings/global.rs
@@ -10,7 +10,7 @@
use dom::bindings::conversions::FromJSValConvertible;
use dom::bindings::js::{JS, JSRef, Root};
use dom::bindings::utils::{Reflectable, Reflector};
-use dom::workerglobalscope::WorkerGlobalScope;
+use dom::workerglobalscope::{WorkerGlobalScope, WorkerGlobalScopeHelpers};
use dom::window;
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
/// thread.
- pub fn script_chan<'b>(&'b self) -> &'b ScriptChan {
+ pub fn script_chan(&self) -> Box<ScriptChan+Send> {
match *self {
GlobalRef::Window(ref window) => window.script_chan(),
GlobalRef::Worker(ref worker) => worker.script_chan(),
diff --git a/components/script/dom/bindings/js.rs b/components/script/dom/bindings/js.rs
index 1f3cb7814e0..a1957f6797a 100644
--- a/components/script/dom/bindings/js.rs
+++ b/components/script/dom/bindings/js.rs
@@ -48,8 +48,6 @@
use dom::bindings::trace::JSTraceable;
use dom::bindings::utils::{Reflector, Reflectable};
use dom::node::Node;
-use dom::xmlhttprequest::{XMLHttpRequest, TrustedXHRAddress};
-use dom::worker::{Worker, TrustedWorkerAddress};
use js::jsapi::JSObject;
use js::jsval::JSVal;
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> {
/// Create a new JS-owned value wrapped from a raw Rust pointer.
pub unsafe fn from_raw(raw: *const T) -> JS<T> {
diff --git a/components/script/dom/bindings/refcounted.rs b/components/script/dom/bindings/refcounted.rs
new file mode 100644
index 00000000000..7bffda2153d
--- /dev/null
+++ b/components/script/dom/bindings/refcounted.rs
@@ -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);
+ }
+}
diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs
index b877d022c2e..955638a7f7d 100644
--- a/components/script/dom/bindings/trace.rs
+++ b/components/script/dom/bindings/trace.rs
@@ -28,8 +28,10 @@
//! a datatype.
use dom::bindings::js::JS;
+use dom::bindings::refcounted::Trusted;
use dom::bindings::utils::{Reflectable, Reflector, WindowProxyHandler};
use dom::node::{Node, TrustedNodeAddress};
+use script_task::ScriptChan;
use collections::hash::{Hash, Hasher};
use cssparser::RGBA;
@@ -203,6 +205,7 @@ no_jsmanaged_fields!(Receiver<T>)
no_jsmanaged_fields!(Rect<T>)
no_jsmanaged_fields!(ImageCacheTask, ScriptControlChan)
no_jsmanaged_fields!(Atom, Namespace, Timer)
+no_jsmanaged_fields!(Trusted<T>)
no_jsmanaged_fields!(PropertyDeclarationBlock)
// These three are interdependent, if you plan to put jsmanaged data
// 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!(RGBA)
+impl JSTraceable for Box<ScriptChan+Send> {
+ #[inline]
+ fn trace(&self, _trc: *mut JSTracer) {
+ // Do nothing
+ }
+}
+
impl<'a> JSTraceable for &'a str {
#[inline]
fn trace(&self, _: *mut JSTracer) {
diff --git a/components/script/dom/dedicatedworkerglobalscope.rs b/components/script/dom/dedicatedworkerglobalscope.rs
index ae0bee5c670..0044bd56254 100644
--- a/components/script/dom/dedicatedworkerglobalscope.rs
+++ b/components/script/dom/dedicatedworkerglobalscope.rs
@@ -2,6 +2,7 @@
* 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 dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::DedicatedWorkerGlobalScopeBinding;
use dom::bindings::codegen::Bindings::DedicatedWorkerGlobalScopeBinding::DedicatedWorkerGlobalScopeMethods;
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::global::GlobalRef;
use dom::bindings::js::{JSRef, Temporary, RootCollection};
+use dom::bindings::refcounted::LiveDOMReferences;
use dom::bindings::utils::Reflectable;
use dom::eventtarget::{EventTarget, EventTargetHelpers, EventTargetTypeId};
use dom::messageevent::MessageEvent;
@@ -34,44 +36,93 @@ use std::rc::Rc;
use std::ptr;
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]
pub struct DedicatedWorkerGlobalScope {
workerglobalscope: WorkerGlobalScope,
- receiver: Receiver<ScriptMsg>,
+ receiver: Receiver<(TrustedWorkerAddress, ScriptMsg)>,
+ own_sender: Sender<(TrustedWorkerAddress, ScriptMsg)>,
+ worker: DOMRefCell<Option<TrustedWorkerAddress>>,
/// Sender to the parent thread.
- parent_sender: ScriptChan,
- worker: TrustedWorkerAddress,
+ parent_sender: Box<ScriptChan+Send>,
}
impl DedicatedWorkerGlobalScope {
fn new_inherited(worker_url: Url,
- worker: TrustedWorkerAddress,
cx: Rc<Cx>,
resource_task: ResourceTask,
- parent_sender: ScriptChan,
- own_sender: ScriptChan,
- receiver: Receiver<ScriptMsg>)
+ parent_sender: Box<ScriptChan+Send>,
+ own_sender: Sender<(TrustedWorkerAddress, ScriptMsg)>,
+ receiver: Receiver<(TrustedWorkerAddress, ScriptMsg)>)
-> DedicatedWorkerGlobalScope {
DedicatedWorkerGlobalScope {
workerglobalscope: WorkerGlobalScope::new_inherited(
- WorkerGlobalScopeTypeId::DedicatedGlobalScope, worker_url, cx,
- resource_task, own_sender),
+ WorkerGlobalScopeTypeId::DedicatedGlobalScope, worker_url, cx, resource_task),
receiver: receiver,
+ own_sender: own_sender,
parent_sender: parent_sender,
- worker: worker,
+ worker: DOMRefCell::new(None),
}
}
pub fn new(worker_url: Url,
- worker: TrustedWorkerAddress,
cx: Rc<Cx>,
resource_task: ResourceTask,
- parent_sender: ScriptChan,
- own_sender: ScriptChan,
- receiver: Receiver<ScriptMsg>)
+ parent_sender: Box<ScriptChan+Send>,
+ own_sender: Sender<(TrustedWorkerAddress, ScriptMsg)>,
+ receiver: Receiver<(TrustedWorkerAddress, ScriptMsg)>)
-> Temporary<DedicatedWorkerGlobalScope> {
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);
DedicatedWorkerGlobalScopeBinding::Wrap(cx.ptr, scope)
}
@@ -81,9 +132,9 @@ impl DedicatedWorkerGlobalScope {
pub fn run_worker_scope(worker_url: Url,
worker: TrustedWorkerAddress,
resource_task: ResourceTask,
- parent_sender: ScriptChan,
- own_sender: ScriptChan,
- receiver: Receiver<ScriptMsg>) {
+ parent_sender: Box<ScriptChan+Send>,
+ own_sender: Sender<(TrustedWorkerAddress, ScriptMsg)>,
+ receiver: Receiver<(TrustedWorkerAddress, ScriptMsg)>) {
spawn_named_native(format!("WebWorker for {}", worker_url.serialize()), proc() {
task_state::initialize(SCRIPT | IN_WORKER);
@@ -103,46 +154,25 @@ impl DedicatedWorkerGlobalScope {
let (_js_runtime, js_context) = ScriptTask::new_rt_and_cx();
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();
- match js_context.evaluate_script(
- global.reflector().get_jsobject(), source, url.serialize(), 1) {
- Ok(_) => (),
- Err(_) => println!("evaluate_script failed")
+
+ {
+ let _ar = AutoWorkerReset::new(*global, worker);
+
+ 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 target: JSRef<EventTarget> =
- EventTargetCast::from_ref(*global);
loop {
match global.receiver.recv_opt() {
- Ok(ScriptMsg::DOMMessage(data, nbytes)) => {
- let mut message = UndefinedValue();
- unsafe {
- 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((linked_worker, msg)) => {
+ let _ar = AutoWorkerReset::new(*global, linked_worker);
+ global.handle_event(msg);
}
- Ok(_) => panic!("Unexpected message"),
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> {
fn PostMessage(self, cx: *mut JSContext, message: JSVal) -> ErrorResult {
let mut data = ptr::null_mut();
@@ -163,25 +245,14 @@ impl<'a> DedicatedWorkerGlobalScopeMethods for JSRef<'a, DedicatedWorkerGlobalSc
return Err(DataClone);
}
- let ScriptChan(ref sender) = self.parent_sender;
- sender.send(ScriptMsg::WorkerPostMessage(self.worker, data, nbytes));
+ let worker = self.worker.borrow().as_ref().unwrap().clone();
+ self.parent_sender.send(ScriptMsg::WorkerPostMessage(worker, data, nbytes));
Ok(())
}
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 {
fn is_dedicatedworkerglobalscope(&self) -> bool {
match *self.type_id() {
diff --git a/components/script/dom/htmlformelement.rs b/components/script/dom/htmlformelement.rs
index dbc67347596..e58aaed0650 100644
--- a/components/script/dom/htmlformelement.rs
+++ b/components/script/dom/htmlformelement.rs
@@ -204,8 +204,7 @@ impl<'a> HTMLFormElementHelpers for JSRef<'a, HTMLFormElement> {
}
// This is wrong. https://html.spec.whatwg.org/multipage/forms.html#planned-navigation
- let ScriptChan(ref script_chan) = *win.script_chan();
- script_chan.send(ScriptMsg::TriggerLoad(win.page().id, load_data));
+ win.script_chan().send(ScriptMsg::TriggerLoad(win.page().id, load_data));
}
fn get_form_dataset<'b>(self, submitter: Option<FormSubmitter<'b>>) -> Vec<FormDatum> {
diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs
index 21afb9e59af..7b7cc679461 100644
--- a/components/script/dom/window.rs
+++ b/components/script/dom/window.rs
@@ -52,7 +52,7 @@ use time;
#[dom_struct]
pub struct Window {
eventtarget: EventTarget,
- script_chan: ScriptChan,
+ script_chan: Box<ScriptChan+Send>,
control_chan: ScriptControlChan,
console: MutNullableJS<Console>,
location: MutNullableJS<Location>,
@@ -75,8 +75,8 @@ impl Window {
(*js_info.as_ref().unwrap().js_context).ptr
}
- pub fn script_chan<'a>(&'a self) -> &'a ScriptChan {
- &self.script_chan
+ pub fn script_chan(&self) -> Box<ScriptChan+Send> {
+ self.script_chan.clone()
}
pub fn control_chan<'a>(&'a self) -> &'a ScriptControlChan {
@@ -189,8 +189,7 @@ impl<'a> WindowMethods for JSRef<'a, Window> {
}
fn Close(self) {
- let ScriptChan(ref chan) = self.script_chan;
- chan.send(ScriptMsg::ExitWindow(self.page.id.clone()));
+ self.script_chan.send(ScriptMsg::ExitWindow(self.page.id.clone()));
}
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());
// FIXME: handle URL parse errors more gracefully.
let url = url.unwrap();
- let ScriptChan(ref script_chan) = self.script_chan;
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 {
- 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 {
pub fn new(cx: *mut JSContext,
page: Rc<Page>,
- script_chan: ScriptChan,
+ script_chan: Box<ScriptChan+Send>,
control_chan: ScriptControlChan,
compositor: Box<ScriptListener+'static>,
image_cache_task: ImageCacheTask)
diff --git a/components/script/dom/worker.rs b/components/script/dom/worker.rs
index 4d03d312223..d6e4391cbfd 100644
--- a/components/script/dom/worker.rs
+++ b/components/script/dom/worker.rs
@@ -9,7 +9,8 @@ use dom::bindings::codegen::InheritTypes::EventTargetCast;
use dom::bindings::error::{Fallible, ErrorResult};
use dom::bindings::error::Error::{Syntax, DataClone};
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::utils::{Reflectable, reflect_dom_object};
use dom::dedicatedworkerglobalscope::DedicatedWorkerGlobalScope;
@@ -20,17 +21,16 @@ use script_task::{ScriptChan, ScriptMsg};
use servo_util::str::DOMString;
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::jsval::{JSVal, UndefinedValue};
use url::UrlParser;
-use libc::{c_void, size_t};
+use libc::size_t;
use std::cell::Cell;
use std::ptr;
-pub struct TrustedWorkerAddress(pub *const c_void);
-no_jsmanaged_fields!(TrustedWorkerAddress)
+pub type TrustedWorkerAddress = Trusted<Worker>;
#[dom_struct]
pub struct Worker {
@@ -39,11 +39,11 @@ pub struct Worker {
global: GlobalField,
/// Sender to the Receiver associated with the DedicatedWorkerGlobalScope
/// this Worker created.
- sender: ScriptChan,
+ sender: Sender<(TrustedWorkerAddress, ScriptMsg)>,
}
impl Worker {
- fn new_inherited(global: &GlobalRef, sender: ScriptChan) -> Worker {
+ fn new_inherited(global: &GlobalRef, sender: Sender<(TrustedWorkerAddress, ScriptMsg)>) -> Worker {
Worker {
eventtarget: EventTarget::new_inherited(EventTargetTypeId::Worker),
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),
*global,
WorkerBinding::Wrap)
@@ -68,13 +68,13 @@ impl Worker {
};
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_ref = worker.addref();
+ let worker_ref = Trusted::new(global.get_cx(), *worker, global.script_chan());
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);
Ok(Temporary::from_rooted(*worker))
@@ -82,7 +82,7 @@ impl Worker {
pub fn handle_message(address: TrustedWorkerAddress,
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();
@@ -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> {
fn PostMessage(self, cx: *mut JSContext, message: JSVal) -> ErrorResult {
let mut data = ptr::null_mut();
@@ -144,9 +112,8 @@ impl<'a> WorkerMethods for JSRef<'a, Worker> {
return Err(DataClone);
}
- self.addref();
- let ScriptChan(ref sender) = self.sender;
- sender.send(ScriptMsg::DOMMessage(data, nbytes));
+ let address = Trusted::new(cx, self, self.global.root().root_ref().script_chan().clone());
+ self.sender.send((address, ScriptMsg::DOMMessage(data, nbytes)));
Ok(())
}
diff --git a/components/script/dom/workerglobalscope.rs b/components/script/dom/workerglobalscope.rs
index b9e1709ca44..3cec2779de9 100644
--- a/components/script/dom/workerglobalscope.rs
+++ b/components/script/dom/workerglobalscope.rs
@@ -2,14 +2,16 @@
* 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 dom::bindings::codegen::Bindings::WorkerGlobalScopeBinding::WorkerGlobalScopeMethods;
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::Error::{Syntax, Network, FailureUnknown};
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{MutNullableJS, JSRef, Temporary};
use dom::bindings::utils::Reflectable;
use dom::console::Console;
+use dom::dedicatedworkerglobalscope::{DedicatedWorkerGlobalScope, DedicatedWorkerGlobalScopeHelpers};
use dom::eventtarget::{EventTarget, EventTargetTypeId};
use dom::workerlocation::WorkerLocation;
use dom::workernavigator::WorkerNavigator;
@@ -40,7 +42,6 @@ pub struct WorkerGlobalScope {
worker_url: Url,
js_context: Rc<Cx>,
resource_task: ResourceTask,
- script_chan: ScriptChan,
location: MutNullableJS<WorkerLocation>,
navigator: MutNullableJS<WorkerNavigator>,
console: MutNullableJS<Console>,
@@ -51,18 +52,16 @@ impl WorkerGlobalScope {
pub fn new_inherited(type_id: WorkerGlobalScopeTypeId,
worker_url: Url,
cx: Rc<Cx>,
- resource_task: ResourceTask,
- script_chan: ScriptChan) -> WorkerGlobalScope {
+ resource_task: ResourceTask) -> WorkerGlobalScope {
WorkerGlobalScope {
eventtarget: EventTarget::new_inherited(EventTargetTypeId::WorkerGlobalScope(type_id)),
worker_url: worker_url,
js_context: cx,
resource_task: resource_task,
- script_chan: script_chan,
location: Default::default(),
navigator: 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 {
&self.worker_url
}
-
- pub fn script_chan<'a>(&'a self) -> &'a ScriptChan {
- &self.script_chan
- }
}
impl<'a> WorkerGlobalScopeMethods for JSRef<'a, WorkerGlobalScope> {
@@ -153,7 +148,7 @@ impl<'a> WorkerGlobalScopeMethods for JSRef<'a, WorkerGlobalScope> {
timeout,
IsInterval::NonInterval,
TimerSource::FromWorker,
- self.script_chan.clone())
+ self.script_chan())
}
fn ClearTimeout(self, handle: i32) {
@@ -166,7 +161,7 @@ impl<'a> WorkerGlobalScopeMethods for JSRef<'a, WorkerGlobalScope> {
timeout,
IsInterval::Interval,
TimerSource::FromWorker,
- self.script_chan.clone())
+ self.script_chan())
}
fn ClearInterval(self, handle: i32) {
@@ -176,13 +171,26 @@ impl<'a> WorkerGlobalScopeMethods for JSRef<'a, WorkerGlobalScope> {
pub trait WorkerGlobalScopeHelpers {
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> {
+ 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) {
self.timers.fire_timer(timer_id, self);
}
+ fn get_cx(self) -> *mut JSContext {
+ self.js_context.ptr
+ }
}
diff --git a/components/script/dom/xmlhttprequest.rs b/components/script/dom/xmlhttprequest.rs
index 9c31ff28d9f..b50a5c4330a 100644
--- a/components/script/dom/xmlhttprequest.rs
+++ b/components/script/dom/xmlhttprequest.rs
@@ -15,6 +15,7 @@ use dom::bindings::error::Error::{InvalidState, InvalidAccess};
use dom::bindings::error::Error::{Network, Syntax, Security, Abort, Timeout};
use dom::bindings::global::{GlobalField, GlobalRef, GlobalRoot};
use dom::bindings::js::{MutNullableJS, JS, JSRef, Temporary, OptionalRootedRootable};
+use dom::bindings::refcounted::Trusted;
use dom::bindings::str::ByteString;
use dom::bindings::utils::{Reflectable, reflect_dom_object};
use dom::document::Document;
@@ -37,13 +38,10 @@ use hyper::http::RawStatus;
use hyper::mime::{mod, Mime};
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::jsval::{JSVal, NullValue, UndefinedValue};
-use libc;
-use libc::c_void;
-
use net::resource_task::{ResourceTask, ResourceCORSData, Load, LoadData, LoadResponse, Payload, Done};
use cors::{allow_cross_origin_request, CORSRequest, RequestMode};
use servo_util::str::DOMString;
@@ -73,15 +71,6 @@ enum XMLHttpRequestState {
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 {
addr: TrustedXHRAddress,
progress: XHRProgress,
@@ -95,7 +84,7 @@ impl XHRProgressHandler {
impl Runnable for XHRProgressHandler {
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> {
Sync(JSRef<'a, XMLHttpRequest>),
- Async(TrustedXHRAddress, &'a ScriptChan)
+ Async(TrustedXHRAddress, Box<ScriptChan+Send>)
}
enum TerminateReason {
@@ -162,7 +151,6 @@ pub struct XMLHttpRequest {
send_flag: Cell<bool>,
global: GlobalField,
- pinned_count: Cell<uint>,
timer: DOMRefCell<Timer>,
fetch_time: Cell<i64>,
terminate_sender: DOMRefCell<Option<Sender<TerminateReason>>>,
@@ -196,7 +184,6 @@ impl XMLHttpRequest {
upload_events: Cell::new(false),
global: GlobalField::from_rooted(global),
- pinned_count: Cell::new(0),
timer: DOMRefCell::new(Timer::new().unwrap()),
fetch_time: Cell::new(0),
terminate_sender: DOMRefCell::new(None),
@@ -213,14 +200,8 @@ impl XMLHttpRequest {
}
pub fn handle_progress(addr: TrustedXHRAddress, progress: XHRProgress) {
- unsafe {
- let xhr = JS::from_trusted_xhr_address(addr).root();
- xhr.process_partial_response(progress);
- }
- }
-
- pub fn handle_release(addr: TrustedXHRAddress) {
- addr.release_once();
+ let xhr = addr.to_temporary().root();
+ xhr.process_partial_response(progress);
}
fn fetch(fetch_type: &SyncOrAsync, resource_task: ResourceTask,
@@ -233,9 +214,8 @@ impl XMLHttpRequest {
SyncOrAsync::Sync(xhr) => {
xhr.process_partial_response(msg);
},
- SyncOrAsync::Async(addr, script_chan) => {
- let ScriptChan(ref chan) = *script_chan;
- chan.send(ScriptMsg::RunnableMsg(box XHRProgressHandler::new(addr, msg)));
+ SyncOrAsync::Async(ref addr, ref script_chan) => {
+ script_chan.send(ScriptMsg::RunnableMsg(box XHRProgressHandler::new(addr.clone(), msg)));
}
}
}
@@ -633,25 +613,20 @@ impl<'a> XMLHttpRequestMethods for JSRef<'a, XMLHttpRequest> {
terminate_receiver, cors_request, gen_id, start_port);
} else {
self.fetch_time.set(time::now().to_timespec().sec);
- let script_chan = global.root_ref().script_chan().clone();
- // Pin the object before launching the fetch task.
- // The `ScriptMsg::RunnableMsg` sent when the fetch task completes will
- // unpin it. This is to ensure that the object will stay alive
- // as long as there are (possibly cancelled) inflight events queued up
- // in the script task's port
- let addr = unsafe {
- self.to_trusted()
- };
+ let script_chan = global.root_ref().script_chan();
+ // Pin the object before launching the fetch task. This is to ensure that
+ // the object will stay alive as long as there are (possibly cancelled)
+ // inflight events queued up in the script task's port.
+ let addr = Trusted::new(self.global.root().root_ref().get_cx(), self,
+ script_chan.clone());
spawn_named("XHRTask", proc() {
- let _ = XMLHttpRequest::fetch(&mut SyncOrAsync::Async(addr, &script_chan),
+ let _ = XMLHttpRequest::fetch(&mut SyncOrAsync::Async(addr, script_chan),
resource_task,
load_data,
terminate_receiver,
cors_request,
gen_id,
start_port);
- let ScriptChan(ref chan) = script_chan;
- chan.send(ScriptMsg::RunnableMsg(box XHRReleaseHandler(addr)));
});
let timeout = self.timeout.get();
if timeout > 0 {
@@ -768,20 +743,9 @@ impl XMLHttpRequestDerived for EventTarget {
}
}
-pub struct TrustedXHRAddress(pub *const c_void);
-
-impl TrustedXHRAddress {
- pub fn release_once(self) {
- unsafe {
- JS::from_trusted_xhr_address(self).root().release_once();
- }
- }
-}
-
+pub type TrustedXHRAddress = Trusted<XMLHttpRequest>;
trait PrivateXMLHttpRequestHelpers {
- unsafe fn to_trusted(self) -> TrustedXHRAddress;
- fn release_once(self);
fn change_ready_state(self, XMLHttpRequestState);
fn process_partial_response(self, progress: XHRProgress);
fn terminate_ongoing_fetch(self);
@@ -796,33 +760,6 @@ trait PrivateXMLHttpRequestHelpers {
}
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) {
assert!(self.ready_state.get() != rs)
self.ready_state.set(rs);
diff --git a/components/script/lib.rs b/components/script/lib.rs
index b62b137a38e..7ad317a1296 100644
--- a/components/script/lib.rs
+++ b/components/script/lib.rs
@@ -59,6 +59,7 @@ pub mod dom {
pub mod cell;
pub mod global;
pub mod js;
+ pub mod refcounted;
pub mod utils;
pub mod callback;
pub mod error;
diff --git a/components/script/script_task.rs b/components/script/script_task.rs
index 26b5665962e..443bb8c37d6 100644
--- a/components/script/script_task.rs
+++ b/components/script/script_task.rs
@@ -15,6 +15,7 @@ use dom::bindings::conversions::FromJSValConvertible;
use dom::bindings::conversions::StringificationBehavior;
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{JS, JSRef, RootCollection, Temporary, OptionalRootable};
+use dom::bindings::refcounted::LiveDOMReferences;
use dom::bindings::trace::JSTraceable;
use dom::bindings::utils::{wrap_for_same_compartment, pre_wrap};
use dom::document::{Document, IsHTMLDocument, DocumentHelpers, DocumentSource};
@@ -63,13 +64,14 @@ use geom::point::Point2D;
use hyper::header::{Header, HeaderFormat};
use hyper::header::common::util as header_util;
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_SetGCCallback, JSGCStatus, JSGC_BEGIN, JSGC_END};
use js::rust::{Cx, RtUtils};
use js;
use url::Url;
+use libc;
use libc::size_t;
use std::any::{Any, AnyRefExt};
use std::comm::{channel, Sender, Receiver, Select};
@@ -114,23 +116,41 @@ pub enum ScriptMsg {
DOMMessage(*mut u64, size_t),
/// Posts a message to the Worker object (dispatched to all tasks).
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.
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.
-#[deriving(Clone)]
-pub struct ScriptChan(pub Sender<ScriptMsg>);
+#[jstraceable]
+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);
+ }
+
+ fn clone(&self) -> Box<ScriptChan+Send> {
+ let NonWorkerScriptChan(ref chan) = *self;
+ box NonWorkerScriptChan(chan.clone())
+ }
+}
-impl ScriptChan {
+impl NonWorkerScriptChan {
/// Creates a new script chan.
- pub fn new() -> (Receiver<ScriptMsg>, ScriptChan) {
+ pub fn new() -> (Receiver<ScriptMsg>, Box<NonWorkerScriptChan>) {
let (chan, port) = channel();
- (port, ScriptChan(chan))
+ (port, box NonWorkerScriptChan(chan))
}
}
@@ -165,7 +185,7 @@ pub struct ScriptTask {
port: Receiver<ScriptMsg>,
/// A channel to hand out to script task-based entities that need to be able to enqueue
/// 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.
control_chan: ScriptControlChan,
@@ -280,7 +300,7 @@ impl ScriptTaskFactory for ScriptTask {
box compositor as Box<ScriptListener>,
layout_chan,
script_port,
- ScriptChan(script_chan),
+ NonWorkerScriptChan(script_chan),
control_chan,
control_port,
constellation_chan,
@@ -312,7 +332,7 @@ impl ScriptTask {
compositor: Box<ScriptListener+'static>,
layout_chan: LayoutChan,
port: Receiver<ScriptMsg>,
- chan: ScriptChan,
+ chan: NonWorkerScriptChan,
control_chan: ScriptControlChan,
control_port: Receiver<ConstellationControlMsg>,
constellation_chan: ConstellationChan,
@@ -368,6 +388,7 @@ impl ScriptTask {
}
pub fn new_rt_and_cx() -> (js::rust::rt, Rc<Cx>) {
+ LiveDOMReferences::initialize();
let js_runtime = js::rust::rt();
assert!({
let ptr: *mut JSRuntime = (*js_runtime).ptr;
@@ -577,10 +598,10 @@ impl ScriptTask {
panic!("unexpected message"),
ScriptMsg::WorkerPostMessage(addr, data, nbytes) =>
Worker::handle_message(addr, data, nbytes),
- ScriptMsg::WorkerRelease(addr) =>
- Worker::handle_release(addr),
ScriptMsg::RunnableMsg(runnable) =>
runnable.handler(),
+ ScriptMsg::RefcountCleanup(addr) =>
+ LiveDOMReferences::cleanup(self.get_cx(), addr),
}
}
diff --git a/components/script/timers.rs b/components/script/timers.rs
index 082ea626790..0502bdfcd38 100644
--- a/components/script/timers.rs
+++ b/components/script/timers.rs
@@ -100,7 +100,7 @@ impl TimerManager {
timeout: i32,
is_interval: IsInterval,
source: TimerSource,
- script_chan: ScriptChan)
+ script_chan: Box<ScriptChan+Send>)
-> i32 {
let timeout = cmp::max(0, timeout) as u64;
let handle = self.next_timer_handle.get();
@@ -136,8 +136,7 @@ impl TimerManager {
let id = select.wait();
if id == timeout_handle.id() {
timeout_port.recv();
- let ScriptChan(ref chan) = script_chan;
- chan.send(ScriptMsg::FireTimer(source, TimerId(handle)));
+ script_chan.send(ScriptMsg::FireTimer(source, TimerId(handle)));
if is_interval == IsInterval::NonInterval {
break;
}