mirror of
https://github.com/servo/servo.git
synced 2025-08-07 06:25:32 +01:00
Upgrade to SM 39
This commit is contained in:
parent
a256f39796
commit
675267b782
205 changed files with 6546 additions and 5340 deletions
|
@ -6,15 +6,22 @@
|
|||
|
||||
use dom::bindings::error::{Fallible, Error};
|
||||
use dom::bindings::global::global_object_for_js_object;
|
||||
use dom::bindings::js::JSRef;
|
||||
use dom::bindings::utils::Reflectable;
|
||||
use js::jsapi::{JSContext, JSObject, JS_WrapObject, JS_ObjectIsCallable, JS_GetGlobalObject};
|
||||
use js::jsapi::{JSContext, JSObject, JS_WrapObject, IsCallable};
|
||||
use js::jsapi::{JS_GetProperty, JS_IsExceptionPending, JS_ReportPendingException};
|
||||
use js::jsapi::{RootedObject, RootedValue, MutableHandleObject, Heap};
|
||||
use js::jsapi::{JSAutoCompartment};
|
||||
use js::jsapi::{JS_BeginRequest, JS_EndRequest};
|
||||
use js::jsapi::{JS_EnterCompartment, JS_LeaveCompartment, JSCompartment};
|
||||
use js::jsapi::GetGlobalForObjectCrossCompartment;
|
||||
use js::jsapi::{JS_SaveFrameChain, JS_RestoreFrameChain};
|
||||
use js::jsval::{JSVal, UndefinedValue};
|
||||
use js::rust::with_compartment;
|
||||
|
||||
use std::ffi::CString;
|
||||
use std::ptr;
|
||||
use std::rc::Rc;
|
||||
use std::intrinsics::return_address;
|
||||
use std::default::Default;
|
||||
|
||||
/// The exception handling used for a call.
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
|
@ -26,7 +33,7 @@ pub enum ExceptionHandling {
|
|||
}
|
||||
|
||||
/// A common base class for representing IDL callback function types.
|
||||
#[derive(Copy, Clone,PartialEq)]
|
||||
#[derive(PartialEq)]
|
||||
#[jstraceable]
|
||||
pub struct CallbackFunction {
|
||||
object: CallbackObject
|
||||
|
@ -34,17 +41,23 @@ pub struct CallbackFunction {
|
|||
|
||||
impl CallbackFunction {
|
||||
/// Create a new `CallbackFunction` for this object.
|
||||
pub fn new(callback: *mut JSObject) -> CallbackFunction {
|
||||
pub fn new() -> CallbackFunction {
|
||||
CallbackFunction {
|
||||
object: CallbackObject {
|
||||
callback: callback
|
||||
callback: Heap::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Initialize the callback function with a value.
|
||||
/// Should be called once this object is done moving.
|
||||
pub fn init(&mut self, callback: *mut JSObject) {
|
||||
self.object.callback.set(callback);
|
||||
}
|
||||
}
|
||||
|
||||
/// A common base class for representing IDL callback interface types.
|
||||
#[derive(Copy, Clone,PartialEq)]
|
||||
#[derive(PartialEq)]
|
||||
#[jstraceable]
|
||||
pub struct CallbackInterface {
|
||||
object: CallbackObject
|
||||
|
@ -53,18 +66,23 @@ pub struct CallbackInterface {
|
|||
/// A common base class for representing IDL callback function and
|
||||
/// callback interface types.
|
||||
#[allow(raw_pointer_derive)]
|
||||
#[derive(Copy, Clone,PartialEq)]
|
||||
#[jstraceable]
|
||||
struct CallbackObject {
|
||||
/// The underlying `JSObject`.
|
||||
callback: *mut JSObject,
|
||||
callback: Heap<*mut JSObject>,
|
||||
}
|
||||
|
||||
impl PartialEq for CallbackObject {
|
||||
fn eq(&self, other: &CallbackObject) -> bool {
|
||||
self.callback.get() == other.callback.get()
|
||||
}
|
||||
}
|
||||
|
||||
/// A trait to be implemented by concrete IDL callback function and
|
||||
/// callback interface types.
|
||||
pub trait CallbackContainer {
|
||||
/// Create a new CallbackContainer object for the given `JSObject`.
|
||||
fn new(callback: *mut JSObject) -> Self;
|
||||
fn new(callback: *mut JSObject) -> Rc<Self>;
|
||||
/// Returns the underlying `JSObject`.
|
||||
fn callback(&self) -> *mut JSObject;
|
||||
}
|
||||
|
@ -72,83 +90,103 @@ pub trait CallbackContainer {
|
|||
impl CallbackInterface {
|
||||
/// Returns the underlying `JSObject`.
|
||||
pub fn callback(&self) -> *mut JSObject {
|
||||
self.object.callback
|
||||
self.object.callback.get()
|
||||
}
|
||||
}
|
||||
|
||||
impl CallbackFunction {
|
||||
/// Returns the underlying `JSObject`.
|
||||
pub fn callback(&self) -> *mut JSObject {
|
||||
self.object.callback
|
||||
self.object.callback.get()
|
||||
}
|
||||
}
|
||||
|
||||
impl CallbackInterface {
|
||||
/// Create a new CallbackInterface object for the given `JSObject`.
|
||||
pub fn new(callback: *mut JSObject) -> CallbackInterface {
|
||||
pub fn new() -> CallbackInterface {
|
||||
CallbackInterface {
|
||||
object: CallbackObject {
|
||||
callback: callback
|
||||
callback: Heap::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Initialize the callback function with a value.
|
||||
/// Should be called once this object is done moving.
|
||||
pub fn init(&mut self, callback: *mut JSObject) {
|
||||
self.object.callback.set(callback);
|
||||
}
|
||||
|
||||
/// Returns the property with the given `name`, if it is a callable object,
|
||||
/// or an error otherwise.
|
||||
pub fn get_callable_property(&self, cx: *mut JSContext, name: &str)
|
||||
-> Fallible<JSVal> {
|
||||
let mut callable = UndefinedValue();
|
||||
let mut callable = RootedValue::new(cx, UndefinedValue());
|
||||
let obj = RootedObject::new(cx, self.callback());
|
||||
unsafe {
|
||||
let c_name = CString::new(name).unwrap();
|
||||
if JS_GetProperty(cx, self.callback(), c_name.as_ptr(), &mut callable) == 0 {
|
||||
if JS_GetProperty(cx, obj.handle(), c_name.as_ptr(),
|
||||
callable.handle_mut()) == 0 {
|
||||
return Err(Error::JSFailed);
|
||||
}
|
||||
|
||||
if !callable.is_object() ||
|
||||
JS_ObjectIsCallable(cx, callable.to_object()) == 0 {
|
||||
if !callable.ptr.is_object() ||
|
||||
IsCallable(callable.ptr.to_object()) == 0 {
|
||||
return Err(Error::Type(
|
||||
format!("The value of the {} property is not callable", name)));
|
||||
}
|
||||
}
|
||||
Ok(callable)
|
||||
Ok(callable.ptr)
|
||||
}
|
||||
}
|
||||
|
||||
/// Wraps the reflector for `p` into the compartment of `cx`.
|
||||
pub fn wrap_call_this_object<T: Reflectable>(cx: *mut JSContext,
|
||||
p: JSRef<T>) -> *mut JSObject {
|
||||
let mut obj = p.reflector().get_jsobject();
|
||||
assert!(!obj.is_null());
|
||||
p: &T,
|
||||
mut rval: MutableHandleObject) {
|
||||
rval.set(p.reflector().get_jsobject().get());
|
||||
assert!(!rval.get().is_null());
|
||||
|
||||
unsafe {
|
||||
if JS_WrapObject(cx, &mut obj) == 0 {
|
||||
return ptr::null_mut();
|
||||
if JS_WrapObject(cx, rval) == 0 {
|
||||
rval.set(ptr::null_mut());
|
||||
}
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// A class that performs whatever setup we need to safely make a call while
|
||||
/// this class is on the stack. After `new` returns, the call is safe to make.
|
||||
pub struct CallSetup {
|
||||
/// The compartment for reporting exceptions.
|
||||
/// As a RootedObject, this must be the first field in order to
|
||||
/// determine the final address on the stack correctly.
|
||||
exception_compartment: RootedObject,
|
||||
/// The `JSContext` used for the call.
|
||||
cx: *mut JSContext,
|
||||
/// The compartment we were in before the call.
|
||||
old_compartment: *mut JSCompartment,
|
||||
/// The exception handling used for the call.
|
||||
_handling: ExceptionHandling,
|
||||
handling: ExceptionHandling,
|
||||
}
|
||||
|
||||
impl CallSetup {
|
||||
/// Performs the setup needed to make a call.
|
||||
#[allow(unrooted_must_root)]
|
||||
pub fn new<T: CallbackContainer>(callback: T, handling: ExceptionHandling) -> CallSetup {
|
||||
pub fn new<T: CallbackContainer>(callback: &T, handling: ExceptionHandling) -> CallSetup {
|
||||
let global = global_object_for_js_object(callback.callback());
|
||||
let global = global.root();
|
||||
let cx = global.r().get_cx();
|
||||
unsafe { JS_BeginRequest(cx); }
|
||||
|
||||
let exception_compartment = unsafe {
|
||||
GetGlobalForObjectCrossCompartment(callback.callback())
|
||||
};
|
||||
CallSetup {
|
||||
exception_compartment:
|
||||
RootedObject::new_with_addr(cx, exception_compartment,
|
||||
unsafe { return_address() }),
|
||||
cx: cx,
|
||||
_handling: handling,
|
||||
old_compartment: unsafe { JS_EnterCompartment(cx, callback.callback()) },
|
||||
handling: handling,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -160,14 +198,23 @@ impl CallSetup {
|
|||
|
||||
impl Drop for CallSetup {
|
||||
fn drop(&mut self) {
|
||||
let need_to_deal_with_exception = unsafe { JS_IsExceptionPending(self.cx) } != 0;
|
||||
unsafe { JS_LeaveCompartment(self.cx, self.old_compartment); }
|
||||
let need_to_deal_with_exception =
|
||||
self.handling == ExceptionHandling::Report &&
|
||||
unsafe { JS_IsExceptionPending(self.cx) } != 0;
|
||||
if need_to_deal_with_exception {
|
||||
unsafe {
|
||||
let old_global = JS_GetGlobalObject(self.cx);
|
||||
with_compartment(self.cx, old_global, || {
|
||||
JS_ReportPendingException(self.cx)
|
||||
});
|
||||
let old_global = RootedObject::new(self.cx, self.exception_compartment.ptr);
|
||||
let saved = JS_SaveFrameChain(self.cx) != 0;
|
||||
{
|
||||
let _ac = JSAutoCompartment::new(self.cx, old_global.ptr);
|
||||
JS_ReportPendingException(self.cx);
|
||||
}
|
||||
if saved {
|
||||
JS_RestoreFrameChain(self.cx);
|
||||
}
|
||||
}
|
||||
}
|
||||
unsafe { JS_EndRequest(self.cx); }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,7 +40,9 @@ impl<T> DOMRefCell<T> {
|
|||
/// so you have to be careful in trace code!
|
||||
#[allow(unsafe_code)]
|
||||
pub unsafe fn borrow_for_gc_trace<'a>(&'a self) -> &'a T {
|
||||
debug_assert!(task_state::get().contains(SCRIPT | IN_GC));
|
||||
// FIXME: IN_GC isn't reliable enough - doesn't catch minor GCs
|
||||
// https://github.com/servo/servo/issues/6389
|
||||
//debug_assert!(task_state::get().contains(SCRIPT | IN_GC));
|
||||
&*self.value.as_unsafe_cell().get()
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
DOMInterfaces = {
|
||||
|
||||
'Window': {
|
||||
'outerObjectHook': 'Some(bindings::utils::outerize_global as extern fn(*mut JSContext, JSHandleObject) -> *mut JSObject)',
|
||||
'outerObjectHook': 'Some(bindings::utils::outerize_global)',
|
||||
},
|
||||
|
||||
#FIXME(jdm): This should be 'register': False, but then we don't generate enum types
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -148,16 +148,16 @@ class Descriptor(DescriptorProvider):
|
|||
if self.interface.isCallback():
|
||||
self.needsRooting = False
|
||||
ty = "%sBinding::%s" % (ifaceName, ifaceName)
|
||||
self.returnType = ty
|
||||
self.returnType = "Rc<%s>"% ty
|
||||
self.argumentType = "???"
|
||||
self.memberType = "???"
|
||||
self.nativeType = ty
|
||||
else:
|
||||
self.needsRooting = True
|
||||
self.returnType = "Temporary<%s>" % ifaceName
|
||||
self.argumentType = "JSRef<%s>" % ifaceName
|
||||
self.returnType = "Root<%s>" % ifaceName
|
||||
self.argumentType = "&%s" % ifaceName
|
||||
self.memberType = "Root<%s>" % ifaceName
|
||||
self.nativeType = "Unrooted<%s>" % ifaceName
|
||||
self.nativeType = "Root<%s>" % ifaceName
|
||||
|
||||
self.concreteType = ifaceName
|
||||
self.register = desc.get('register', True)
|
||||
|
|
|
@ -24,17 +24,17 @@
|
|||
//! | USVString | `USVString` |
|
||||
//! | ByteString | `ByteString` |
|
||||
//! | object | `*mut JSObject` |
|
||||
//! | interface types | `JSRef<T>` | `Temporary<T>` |
|
||||
//! | interface types | `&T` | `Root<T>` |
|
||||
//! | dictionary types | `&T` | *unsupported* |
|
||||
//! | enumeration types | `T` |
|
||||
//! | callback function types | `T` |
|
||||
//! | callback function types | `Rc<T>` |
|
||||
//! | nullable types | `Option<T>` |
|
||||
//! | sequences | `Vec<T>` |
|
||||
//! | union types | `T` |
|
||||
|
||||
use dom::bindings::codegen::PrototypeList;
|
||||
use dom::bindings::error::throw_type_error;
|
||||
use dom::bindings::js::{JSRef, Root, Unrooted};
|
||||
use dom::bindings::js::Root;
|
||||
use dom::bindings::num::Finite;
|
||||
use dom::bindings::str::{ByteString, USVString};
|
||||
use dom::bindings::utils::{Reflectable, Reflector, DOMClass};
|
||||
|
@ -43,14 +43,15 @@ use util::str::DOMString;
|
|||
use js;
|
||||
use js::glue::{RUST_JSID_TO_STRING, RUST_JSID_IS_STRING};
|
||||
use js::glue::RUST_JS_NumberValue;
|
||||
use js::jsapi::{JSBool, JSContext, JSObject, JSString, jsid};
|
||||
use js::jsapi::{JS_ValueToUint64, JS_ValueToInt64};
|
||||
use js::jsapi::{JS_ValueToECMAUint32, JS_ValueToECMAInt32};
|
||||
use js::jsapi::{JS_ValueToUint16, JS_ValueToNumber, JS_ValueToBoolean};
|
||||
use js::jsapi::{JS_ValueToString, JS_GetStringCharsAndLength};
|
||||
use js::rust::{ToUint64, ToInt64};
|
||||
use js::rust::{ToUint32, ToInt32};
|
||||
use js::rust::{ToUint16, ToNumber, ToBoolean, ToString};
|
||||
use js::jsapi::{JSContext, JSObject, JSString};
|
||||
use js::jsapi::{JS_StringHasLatin1Chars, JS_GetLatin1StringCharsAndLength, JS_GetTwoByteStringCharsAndLength};
|
||||
use js::jsapi::{JS_NewUCStringCopyN, JS_NewStringCopyN};
|
||||
use js::jsapi::{JS_WrapValue};
|
||||
use js::jsapi::{JSClass, JS_GetClass};
|
||||
use js::jsapi::{HandleId, HandleValue, HandleObject, MutableHandleValue};
|
||||
use js::jsval::JSVal;
|
||||
use js::jsval::{UndefinedValue, NullValue, BooleanValue, Int32Value, UInt32Value};
|
||||
use js::jsval::{StringValue, ObjectValue, ObjectOrNullValue};
|
||||
|
@ -60,6 +61,9 @@ use num::Float;
|
|||
use std::borrow::ToOwned;
|
||||
use std::default;
|
||||
use std::slice;
|
||||
use std::ptr;
|
||||
use std::rc::Rc;
|
||||
use core::nonzero::NonZero;
|
||||
|
||||
/// A trait to retrieve the constants necessary to check if a `JSObject`
|
||||
/// implements a given interface.
|
||||
|
@ -74,7 +78,7 @@ pub trait IDLInterface {
|
|||
/// A trait to convert Rust types to `JSVal`s.
|
||||
pub trait ToJSValConvertible {
|
||||
/// Convert `self` to a `JSVal`. JSAPI failure causes a task failure.
|
||||
fn to_jsval(&self, cx: *mut JSContext) -> JSVal;
|
||||
fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue);
|
||||
}
|
||||
|
||||
/// A trait to convert `JSVal`s to Rust types.
|
||||
|
@ -85,206 +89,201 @@ pub trait FromJSValConvertible {
|
|||
/// Optional configuration of type `T` can be passed as the `option`
|
||||
/// argument.
|
||||
/// If it returns `Err(())`, a JSAPI exception is pending.
|
||||
fn from_jsval(cx: *mut JSContext, val: JSVal, option: Self::Config) -> Result<Self, ()>;
|
||||
fn from_jsval(cx: *mut JSContext, val: HandleValue, option: Self::Config) -> Result<Self, ()>;
|
||||
}
|
||||
|
||||
|
||||
impl ToJSValConvertible for () {
|
||||
fn to_jsval(&self, _cx: *mut JSContext) -> JSVal {
|
||||
UndefinedValue()
|
||||
fn to_jsval(&self, _cx: *mut JSContext, rval: MutableHandleValue) {
|
||||
rval.set(UndefinedValue());
|
||||
}
|
||||
}
|
||||
|
||||
impl ToJSValConvertible for JSVal {
|
||||
fn to_jsval(&self, cx: *mut JSContext) -> JSVal {
|
||||
let mut value = *self;
|
||||
if unsafe { JS_WrapValue(cx, &mut value) } == 0 {
|
||||
fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) {
|
||||
rval.set(*self);
|
||||
if unsafe { JS_WrapValue(cx, rval) } == 0 {
|
||||
panic!("JS_WrapValue failed.");
|
||||
}
|
||||
value
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn convert_from_jsval<T: default::Default>(
|
||||
cx: *mut JSContext, value: JSVal,
|
||||
convert_fn: unsafe extern "C" fn(*mut JSContext, JSVal, *mut T) -> JSBool) -> Result<T, ()> {
|
||||
let mut ret = default::Default::default();
|
||||
if convert_fn(cx, value, &mut ret) == 0 {
|
||||
Err(())
|
||||
} else {
|
||||
Ok(ret)
|
||||
impl ToJSValConvertible for HandleValue {
|
||||
fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) {
|
||||
rval.set(self.get());
|
||||
if unsafe { JS_WrapValue(cx, rval) } == 0 {
|
||||
panic!("JS_WrapValue failed.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl ToJSValConvertible for bool {
|
||||
fn to_jsval(&self, _cx: *mut JSContext) -> JSVal {
|
||||
BooleanValue(*self)
|
||||
fn to_jsval(&self, _cx: *mut JSContext, rval: MutableHandleValue) {
|
||||
rval.set(BooleanValue(*self));
|
||||
}
|
||||
}
|
||||
|
||||
impl FromJSValConvertible for bool {
|
||||
type Config = ();
|
||||
fn from_jsval(cx: *mut JSContext, val: JSVal, _option: ()) -> Result<bool, ()> {
|
||||
let result = unsafe { convert_from_jsval(cx, val, JS_ValueToBoolean) };
|
||||
result.map(|b| b != 0)
|
||||
fn from_jsval(_cx: *mut JSContext, val: HandleValue, _option: ()) -> Result<bool, ()> {
|
||||
Ok(ToBoolean(val))
|
||||
}
|
||||
}
|
||||
|
||||
impl ToJSValConvertible for i8 {
|
||||
fn to_jsval(&self, _cx: *mut JSContext) -> JSVal {
|
||||
Int32Value(*self as i32)
|
||||
fn to_jsval(&self, _cx: *mut JSContext, rval: MutableHandleValue) {
|
||||
rval.set(Int32Value(*self as i32));
|
||||
}
|
||||
}
|
||||
|
||||
impl FromJSValConvertible for i8 {
|
||||
type Config = ();
|
||||
fn from_jsval(cx: *mut JSContext, val: JSVal, _option: ()) -> Result<i8, ()> {
|
||||
let result = unsafe { convert_from_jsval(cx, val, JS_ValueToECMAInt32) };
|
||||
fn from_jsval(cx: *mut JSContext, val: HandleValue, _option: ()) -> Result<i8, ()> {
|
||||
let result = ToInt32(cx, val);
|
||||
result.map(|v| v as i8)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToJSValConvertible for u8 {
|
||||
fn to_jsval(&self, _cx: *mut JSContext) -> JSVal {
|
||||
Int32Value(*self as i32)
|
||||
fn to_jsval(&self, _cx: *mut JSContext, rval: MutableHandleValue) {
|
||||
rval.set(Int32Value(*self as i32));
|
||||
}
|
||||
}
|
||||
|
||||
impl FromJSValConvertible for u8 {
|
||||
type Config = ();
|
||||
fn from_jsval(cx: *mut JSContext, val: JSVal, _option: ()) -> Result<u8, ()> {
|
||||
let result = unsafe { convert_from_jsval(cx, val, JS_ValueToECMAInt32) };
|
||||
fn from_jsval(cx: *mut JSContext, val: HandleValue, _option: ()) -> Result<u8, ()> {
|
||||
let result = ToInt32(cx, val);
|
||||
result.map(|v| v as u8)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToJSValConvertible for i16 {
|
||||
fn to_jsval(&self, _cx: *mut JSContext) -> JSVal {
|
||||
Int32Value(*self as i32)
|
||||
fn to_jsval(&self, _cx: *mut JSContext, rval: MutableHandleValue) {
|
||||
rval.set(Int32Value(*self as i32));
|
||||
}
|
||||
}
|
||||
|
||||
impl FromJSValConvertible for i16 {
|
||||
type Config = ();
|
||||
fn from_jsval(cx: *mut JSContext, val: JSVal, _option: ()) -> Result<i16, ()> {
|
||||
let result = unsafe { convert_from_jsval(cx, val, JS_ValueToECMAInt32) };
|
||||
fn from_jsval(cx: *mut JSContext, val: HandleValue, _option: ()) -> Result<i16, ()> {
|
||||
let result = ToInt32(cx, val);
|
||||
result.map(|v| v as i16)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToJSValConvertible for u16 {
|
||||
fn to_jsval(&self, _cx: *mut JSContext) -> JSVal {
|
||||
Int32Value(*self as i32)
|
||||
fn to_jsval(&self, _cx: *mut JSContext, rval: MutableHandleValue) {
|
||||
rval.set(Int32Value(*self as i32));
|
||||
}
|
||||
}
|
||||
|
||||
impl FromJSValConvertible for u16 {
|
||||
type Config = ();
|
||||
fn from_jsval(cx: *mut JSContext, val: JSVal, _option: ()) -> Result<u16, ()> {
|
||||
unsafe { convert_from_jsval(cx, val, JS_ValueToUint16) }
|
||||
fn from_jsval(cx: *mut JSContext, val: HandleValue, _option: ()) -> Result<u16, ()> {
|
||||
ToUint16(cx, val)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToJSValConvertible for i32 {
|
||||
fn to_jsval(&self, _cx: *mut JSContext) -> JSVal {
|
||||
Int32Value(*self)
|
||||
fn to_jsval(&self, _cx: *mut JSContext, rval: MutableHandleValue) {
|
||||
rval.set(Int32Value(*self));
|
||||
}
|
||||
}
|
||||
|
||||
impl FromJSValConvertible for i32 {
|
||||
type Config = ();
|
||||
fn from_jsval(cx: *mut JSContext, val: JSVal, _option: ()) -> Result<i32, ()> {
|
||||
unsafe { convert_from_jsval(cx, val, JS_ValueToECMAInt32) }
|
||||
fn from_jsval(cx: *mut JSContext, val: HandleValue, _option: ()) -> Result<i32, ()> {
|
||||
ToInt32(cx, val)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToJSValConvertible for u32 {
|
||||
fn to_jsval(&self, _cx: *mut JSContext) -> JSVal {
|
||||
UInt32Value(*self)
|
||||
fn to_jsval(&self, _cx: *mut JSContext, rval: MutableHandleValue) {
|
||||
rval.set(UInt32Value(*self));
|
||||
}
|
||||
}
|
||||
|
||||
impl FromJSValConvertible for u32 {
|
||||
type Config = ();
|
||||
fn from_jsval(cx: *mut JSContext, val: JSVal, _option: ()) -> Result<u32, ()> {
|
||||
unsafe { convert_from_jsval(cx, val, JS_ValueToECMAUint32) }
|
||||
fn from_jsval(cx: *mut JSContext, val: HandleValue, _option: ()) -> Result<u32, ()> {
|
||||
ToUint32(cx, val)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToJSValConvertible for i64 {
|
||||
fn to_jsval(&self, _cx: *mut JSContext) -> JSVal {
|
||||
fn to_jsval(&self, _cx: *mut JSContext, rval: MutableHandleValue) {
|
||||
unsafe {
|
||||
RUST_JS_NumberValue(*self as f64)
|
||||
rval.set(RUST_JS_NumberValue(*self as f64));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromJSValConvertible for i64 {
|
||||
type Config = ();
|
||||
fn from_jsval(cx: *mut JSContext, val: JSVal, _option: ()) -> Result<i64, ()> {
|
||||
unsafe { convert_from_jsval(cx, val, JS_ValueToInt64) }
|
||||
fn from_jsval(cx: *mut JSContext, val: HandleValue, _option: ()) -> Result<i64, ()> {
|
||||
ToInt64(cx, val)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToJSValConvertible for u64 {
|
||||
fn to_jsval(&self, _cx: *mut JSContext) -> JSVal {
|
||||
fn to_jsval(&self, _cx: *mut JSContext, rval: MutableHandleValue) {
|
||||
unsafe {
|
||||
RUST_JS_NumberValue(*self as f64)
|
||||
rval.set(RUST_JS_NumberValue(*self as f64));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromJSValConvertible for u64 {
|
||||
type Config = ();
|
||||
fn from_jsval(cx: *mut JSContext, val: JSVal, _option: ()) -> Result<u64, ()> {
|
||||
unsafe { convert_from_jsval(cx, val, JS_ValueToUint64) }
|
||||
fn from_jsval(cx: *mut JSContext, val: HandleValue, _option: ()) -> Result<u64, ()> {
|
||||
ToUint64(cx, val)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToJSValConvertible for f32 {
|
||||
fn to_jsval(&self, _cx: *mut JSContext) -> JSVal {
|
||||
fn to_jsval(&self, _cx: *mut JSContext, rval: MutableHandleValue) {
|
||||
unsafe {
|
||||
RUST_JS_NumberValue(*self as f64)
|
||||
rval.set(RUST_JS_NumberValue(*self as f64));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromJSValConvertible for f32 {
|
||||
type Config = ();
|
||||
fn from_jsval(cx: *mut JSContext, val: JSVal, _option: ()) -> Result<f32, ()> {
|
||||
let result = unsafe { convert_from_jsval(cx, val, JS_ValueToNumber) };
|
||||
fn from_jsval(cx: *mut JSContext, val: HandleValue, _option: ()) -> Result<f32, ()> {
|
||||
let result = ToNumber(cx, val);
|
||||
result.map(|f| f as f32)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToJSValConvertible for f64 {
|
||||
fn to_jsval(&self, _cx: *mut JSContext) -> JSVal {
|
||||
fn to_jsval(&self, _cx: *mut JSContext, rval: MutableHandleValue) {
|
||||
unsafe {
|
||||
RUST_JS_NumberValue(*self)
|
||||
rval.set(RUST_JS_NumberValue(*self));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromJSValConvertible for f64 {
|
||||
type Config = ();
|
||||
fn from_jsval(cx: *mut JSContext, val: JSVal, _option: ()) -> Result<f64, ()> {
|
||||
unsafe { convert_from_jsval(cx, val, JS_ValueToNumber) }
|
||||
fn from_jsval(cx: *mut JSContext, val: HandleValue, _option: ()) -> Result<f64, ()> {
|
||||
ToNumber(cx, val)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Float + ToJSValConvertible> ToJSValConvertible for Finite<T> {
|
||||
#[inline]
|
||||
fn to_jsval(&self, cx: *mut JSContext) -> JSVal {
|
||||
fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) {
|
||||
let value = **self;
|
||||
value.to_jsval(cx)
|
||||
value.to_jsval(cx, rval);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Float + FromJSValConvertible<Config=()>> FromJSValConvertible for Finite<T> {
|
||||
type Config = ();
|
||||
|
||||
fn from_jsval(cx: *mut JSContext, value: JSVal, option: ()) -> Result<Finite<T>, ()> {
|
||||
fn from_jsval(cx: *mut JSContext, value: HandleValue, option: ()) -> Result<Finite<T>, ()> {
|
||||
let result = try!(FromJSValConvertible::from_jsval(cx, value, option));
|
||||
match Finite::new(result) {
|
||||
Some(v) => Ok(v),
|
||||
|
@ -297,21 +296,22 @@ impl<T: Float + FromJSValConvertible<Config=()>> FromJSValConvertible for Finite
|
|||
}
|
||||
|
||||
impl ToJSValConvertible for str {
|
||||
fn to_jsval(&self, cx: *mut JSContext) -> JSVal {
|
||||
fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) {
|
||||
unsafe {
|
||||
let string_utf16: Vec<u16> = self.utf16_units().collect();
|
||||
let jsstr = JS_NewUCStringCopyN(cx, string_utf16.as_ptr(), string_utf16.len() as libc::size_t);
|
||||
let jsstr = JS_NewUCStringCopyN(cx, string_utf16.as_ptr() as *const i16,
|
||||
string_utf16.len() as libc::size_t);
|
||||
if jsstr.is_null() {
|
||||
panic!("JS_NewUCStringCopyN failed");
|
||||
}
|
||||
StringValue(&*jsstr)
|
||||
rval.set(StringValue(&*jsstr));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToJSValConvertible for DOMString {
|
||||
fn to_jsval(&self, cx: *mut JSContext) -> JSVal {
|
||||
(**self).to_jsval(cx)
|
||||
fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) {
|
||||
(**self).to_jsval(cx, rval);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -333,18 +333,36 @@ impl default::Default for StringificationBehavior {
|
|||
/// Convert the given `JSString` to a `DOMString`. Fails if the string does not
|
||||
/// contain valid UTF-16.
|
||||
pub fn jsstring_to_str(cx: *mut JSContext, s: *mut JSString) -> DOMString {
|
||||
unsafe {
|
||||
let mut length = 0;
|
||||
let chars = JS_GetStringCharsAndLength(cx, s, &mut length);
|
||||
let mut length = 0;
|
||||
let latin1 = unsafe { JS_StringHasLatin1Chars(s) != 0 };
|
||||
if latin1 {
|
||||
let chars = unsafe {
|
||||
JS_GetLatin1StringCharsAndLength(cx, ptr::null(), s, &mut length)
|
||||
};
|
||||
assert!(!chars.is_null());
|
||||
let char_vec = slice::from_raw_parts(chars, length as usize);
|
||||
|
||||
let mut buf = String::with_capacity(length as usize);
|
||||
for i in 0..(length as isize) {
|
||||
unsafe {
|
||||
buf.push(*chars.offset(i) as char);
|
||||
}
|
||||
}
|
||||
buf
|
||||
} else {
|
||||
let chars = unsafe {
|
||||
JS_GetTwoByteStringCharsAndLength(cx, ptr::null(), s, &mut length)
|
||||
};
|
||||
assert!(!chars.is_null());
|
||||
let char_vec = unsafe {
|
||||
slice::from_raw_parts(chars as *const u16, length as usize)
|
||||
};
|
||||
String::from_utf16(char_vec).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert the given `jsid` to a `DOMString`. Fails if the `jsid` is not a
|
||||
/// string, or if the string does not contain valid UTF-16.
|
||||
pub fn jsid_to_str(cx: *mut JSContext, id: jsid) -> DOMString {
|
||||
pub fn jsid_to_str(cx: *mut JSContext, id: HandleId) -> DOMString {
|
||||
unsafe {
|
||||
assert!(RUST_JSID_IS_STRING(id) != 0);
|
||||
jsstring_to_str(cx, RUST_JSID_TO_STRING(id))
|
||||
|
@ -353,15 +371,16 @@ pub fn jsid_to_str(cx: *mut JSContext, id: jsid) -> DOMString {
|
|||
|
||||
impl FromJSValConvertible for DOMString {
|
||||
type Config = StringificationBehavior;
|
||||
fn from_jsval(cx: *mut JSContext, value: JSVal,
|
||||
fn from_jsval(cx: *mut JSContext, value: HandleValue,
|
||||
null_behavior: StringificationBehavior)
|
||||
-> Result<DOMString, ()> {
|
||||
if null_behavior == StringificationBehavior::Empty && value.is_null() {
|
||||
if null_behavior == StringificationBehavior::Empty &&
|
||||
value.get().is_null() {
|
||||
Ok("".to_owned())
|
||||
} else {
|
||||
let jsstr = unsafe { JS_ValueToString(cx, value) };
|
||||
let jsstr = ToString(cx, value);
|
||||
if jsstr.is_null() {
|
||||
debug!("JS_ValueToString failed");
|
||||
debug!("ToString failed");
|
||||
Err(())
|
||||
} else {
|
||||
Ok(jsstring_to_str(cx, jsstr))
|
||||
|
@ -371,56 +390,75 @@ impl FromJSValConvertible for DOMString {
|
|||
}
|
||||
|
||||
impl ToJSValConvertible for USVString {
|
||||
fn to_jsval(&self, cx: *mut JSContext) -> JSVal {
|
||||
self.0.to_jsval(cx)
|
||||
fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) {
|
||||
self.0.to_jsval(cx, rval);
|
||||
}
|
||||
}
|
||||
|
||||
impl FromJSValConvertible for USVString {
|
||||
type Config = ();
|
||||
fn from_jsval(cx: *mut JSContext, value: JSVal, _: ())
|
||||
fn from_jsval(cx: *mut JSContext, value: HandleValue, _: ())
|
||||
-> Result<USVString, ()> {
|
||||
let jsstr = unsafe { JS_ValueToString(cx, value) };
|
||||
let jsstr = ToString(cx, value);
|
||||
if jsstr.is_null() {
|
||||
debug!("JS_ValueToString failed");
|
||||
Err(())
|
||||
} else {
|
||||
unsafe {
|
||||
let mut length = 0;
|
||||
let chars = JS_GetStringCharsAndLength(cx, jsstr, &mut length);
|
||||
assert!(!chars.is_null());
|
||||
let char_vec = slice::from_raw_parts(chars, length as usize);
|
||||
Ok(USVString(String::from_utf16_lossy(char_vec)))
|
||||
}
|
||||
debug!("ToString failed");
|
||||
return Err(());
|
||||
}
|
||||
let latin1 = unsafe { JS_StringHasLatin1Chars(jsstr) != 0 };
|
||||
if latin1 {
|
||||
return Ok(USVString(jsstring_to_str(cx, jsstr)));
|
||||
}
|
||||
unsafe {
|
||||
let mut length = 0;
|
||||
let chars = JS_GetTwoByteStringCharsAndLength(cx, ptr::null(), jsstr, &mut length);
|
||||
assert!(!chars.is_null());
|
||||
let char_vec = slice::from_raw_parts(chars as *const u16, length as usize);
|
||||
Ok(USVString(String::from_utf16_lossy(char_vec)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToJSValConvertible for ByteString {
|
||||
fn to_jsval(&self, cx: *mut JSContext) -> JSVal {
|
||||
fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) {
|
||||
unsafe {
|
||||
let jsstr = JS_NewStringCopyN(cx, self.as_ptr() as *const libc::c_char,
|
||||
self.len() as libc::size_t);
|
||||
if jsstr.is_null() {
|
||||
panic!("JS_NewStringCopyN failed");
|
||||
}
|
||||
StringValue(&*jsstr)
|
||||
rval.set(StringValue(&*jsstr));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromJSValConvertible for ByteString {
|
||||
type Config = ();
|
||||
fn from_jsval(cx: *mut JSContext, value: JSVal, _option: ()) -> Result<ByteString, ()> {
|
||||
unsafe {
|
||||
let string = JS_ValueToString(cx, value);
|
||||
if string.is_null() {
|
||||
debug!("JS_ValueToString failed");
|
||||
return Err(());
|
||||
}
|
||||
fn from_jsval(cx: *mut JSContext, value: HandleValue, _option: ()) -> Result<ByteString, ()> {
|
||||
let string = ToString(cx, value);
|
||||
if string.is_null() {
|
||||
debug!("ToString failed");
|
||||
return Err(());
|
||||
}
|
||||
|
||||
let latin1 = unsafe { JS_StringHasLatin1Chars(string) != 0 };
|
||||
if latin1 {
|
||||
let mut length = 0;
|
||||
let chars = JS_GetStringCharsAndLength(cx, string, &mut length);
|
||||
let chars = unsafe {
|
||||
JS_GetLatin1StringCharsAndLength(cx, ptr::null(),
|
||||
string, &mut length)
|
||||
};
|
||||
assert!(!chars.is_null());
|
||||
|
||||
let char_vec = unsafe {
|
||||
Vec::from_raw_buf(chars as *mut u8, length as usize)
|
||||
};
|
||||
|
||||
return Ok(ByteString::new(char_vec));
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let mut length = 0;
|
||||
let chars = JS_GetTwoByteStringCharsAndLength(cx, ptr::null(), string, &mut length);
|
||||
let char_vec = slice::from_raw_parts(chars, length as usize);
|
||||
|
||||
if char_vec.iter().any(|&c| c > 0xFF) {
|
||||
|
@ -434,14 +472,13 @@ impl FromJSValConvertible for ByteString {
|
|||
}
|
||||
|
||||
impl ToJSValConvertible for Reflector {
|
||||
fn to_jsval(&self, cx: *mut JSContext) -> JSVal {
|
||||
let obj = self.get_jsobject();
|
||||
fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) {
|
||||
let obj = self.get_jsobject().get();
|
||||
assert!(!obj.is_null());
|
||||
let mut value = ObjectValue(unsafe { &*obj });
|
||||
if unsafe { JS_WrapValue(cx, &mut value) } == 0 {
|
||||
rval.set(ObjectValue(unsafe { &*obj }));
|
||||
if unsafe { JS_WrapValue(cx, rval) } == 0 {
|
||||
panic!("JS_WrapValue failed.");
|
||||
}
|
||||
value
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -454,11 +491,10 @@ pub fn is_dom_class(clasp: *const JSClass) -> bool {
|
|||
|
||||
/// Returns whether `obj` is a DOM object implemented as a proxy.
|
||||
pub fn is_dom_proxy(obj: *mut JSObject) -> bool {
|
||||
use js::glue::{js_IsObjectProxyClass, js_IsFunctionProxyClass, IsProxyHandlerFamily};
|
||||
|
||||
use js::glue::IsProxyHandlerFamily;
|
||||
unsafe {
|
||||
(js_IsObjectProxyClass(obj) || js_IsFunctionProxyClass(obj)) &&
|
||||
IsProxyHandlerFamily(obj)
|
||||
let clasp = JS_GetClass(obj);
|
||||
((*clasp).flags & js::JSCLASS_IS_PROXY) != 0 && IsProxyHandlerFamily(obj) != 0
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -467,29 +503,24 @@ pub fn is_dom_proxy(obj: *mut JSObject) -> bool {
|
|||
// We use slot 0 for holding the raw object. This is safe for both
|
||||
// globals and non-globals.
|
||||
pub const DOM_OBJECT_SLOT: u32 = 0;
|
||||
const DOM_PROXY_OBJECT_SLOT: u32 = js::JSSLOT_PROXY_PRIVATE;
|
||||
|
||||
/// Returns the index of the slot wherein a pointer to the reflected DOM object
|
||||
/// is stored.
|
||||
///
|
||||
/// Fails if `obj` is not a DOM object.
|
||||
pub unsafe fn dom_object_slot(obj: *mut JSObject) -> u32 {
|
||||
let clasp = JS_GetClass(obj);
|
||||
if is_dom_class(&*clasp) {
|
||||
DOM_OBJECT_SLOT
|
||||
} else {
|
||||
assert!(is_dom_proxy(obj));
|
||||
DOM_PROXY_OBJECT_SLOT
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the DOM object from the given reflector.
|
||||
pub unsafe fn native_from_reflector<T>(obj: *mut JSObject) -> *const T {
|
||||
use js::jsapi::JS_GetReservedSlot;
|
||||
use js::glue::GetProxyPrivate;
|
||||
|
||||
let slot = dom_object_slot(obj);
|
||||
let value = JS_GetReservedSlot(obj, slot);
|
||||
value.to_private() as *const T
|
||||
let clasp = JS_GetClass(obj);
|
||||
let value = if is_dom_class(clasp) {
|
||||
JS_GetReservedSlot(obj, DOM_OBJECT_SLOT)
|
||||
} else {
|
||||
assert!(is_dom_proxy(obj));
|
||||
GetProxyPrivate(obj)
|
||||
};
|
||||
if value.is_undefined() {
|
||||
ptr::null()
|
||||
} else {
|
||||
value.to_private() as *const T
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the `DOMClass` from `obj`, or `Err(())` if `obj` is not a DOM object.
|
||||
|
@ -518,17 +549,16 @@ unsafe fn get_dom_class(obj: *mut JSObject) -> Result<DOMClass, ()> {
|
|||
/// Returns Err(()) if `obj` is an opaque security wrapper or if the object is
|
||||
/// not a reflector for a DOM object of the given type (as defined by the
|
||||
/// proto_id and proto_depth).
|
||||
pub fn native_from_reflector_jsmanaged<T>(mut obj: *mut JSObject) -> Result<Unrooted<T>, ()>
|
||||
pub fn native_from_reflector_jsmanaged<T>(mut obj: *mut JSObject) -> Result<Root<T>, ()>
|
||||
where T: Reflectable + IDLInterface
|
||||
{
|
||||
use js::glue::{IsWrapper, UnwrapObject};
|
||||
use std::ptr;
|
||||
|
||||
unsafe {
|
||||
let dom_class = try!(get_dom_class(obj).or_else(|_| {
|
||||
if IsWrapper(obj) == 1 {
|
||||
debug!("found wrapper");
|
||||
obj = UnwrapObject(obj, /* stopAtOuter = */ 0, ptr::null_mut());
|
||||
obj = UnwrapObject(obj, /* stopAtOuter = */ 0);
|
||||
if obj.is_null() {
|
||||
debug!("unwrapping security wrapper failed");
|
||||
Err(())
|
||||
|
@ -547,7 +577,9 @@ pub fn native_from_reflector_jsmanaged<T>(mut obj: *mut JSObject) -> Result<Unro
|
|||
let proto_depth = <T as IDLInterface>::get_prototype_depth();
|
||||
if dom_class.interface_chain[proto_depth] == proto_id {
|
||||
debug!("good prototype");
|
||||
Ok(Unrooted::from_raw(native_from_reflector(obj)))
|
||||
let native = native_from_reflector(obj);
|
||||
assert!(!native.is_null());
|
||||
Ok(Root::new(NonZero::new(native)))
|
||||
} else {
|
||||
debug!("bad prototype");
|
||||
Err(())
|
||||
|
@ -555,37 +587,54 @@ pub fn native_from_reflector_jsmanaged<T>(mut obj: *mut JSObject) -> Result<Unro
|
|||
}
|
||||
}
|
||||
|
||||
/// Get a Rooted<T> for a DOM object accessible from a HandleValue
|
||||
pub fn native_from_handlevalue<T>(v: HandleValue) -> Result<Root<T>, ()>
|
||||
where T: Reflectable + IDLInterface
|
||||
{
|
||||
native_from_reflector_jsmanaged(v.get().to_object())
|
||||
}
|
||||
|
||||
/// Get a Rooted<T> for a DOM object accessible from a HandleObject
|
||||
pub fn native_from_handleobject<T>(obj: HandleObject) -> Result<Root<T>, ()>
|
||||
where T: Reflectable + IDLInterface
|
||||
{
|
||||
native_from_reflector_jsmanaged(obj.get())
|
||||
}
|
||||
|
||||
impl<T: Reflectable> ToJSValConvertible for Root<T> {
|
||||
fn to_jsval(&self, cx: *mut JSContext) -> JSVal {
|
||||
self.r().reflector().to_jsval(cx)
|
||||
fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) {
|
||||
self.r().reflector().to_jsval(cx, rval);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Reflectable> ToJSValConvertible for JSRef<'a, T> {
|
||||
fn to_jsval(&self, cx: *mut JSContext) -> JSVal {
|
||||
self.reflector().to_jsval(cx)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Reflectable> ToJSValConvertible for Unrooted<T> {
|
||||
fn to_jsval(&self, cx: *mut JSContext) -> JSVal {
|
||||
self.reflector().to_jsval(cx)
|
||||
impl<'a, T: Reflectable> ToJSValConvertible for &'a T {
|
||||
fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) {
|
||||
self.reflector().to_jsval(cx, rval);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ToJSValConvertible> ToJSValConvertible for Option<T> {
|
||||
fn to_jsval(&self, cx: *mut JSContext) -> JSVal {
|
||||
fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) {
|
||||
match self {
|
||||
&Some(ref value) => value.to_jsval(cx),
|
||||
&None => NullValue(),
|
||||
&Some(ref value) => value.to_jsval(cx, rval),
|
||||
&None => rval.set(NullValue()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ToJSValConvertible> ToJSValConvertible for Option<Rc<T>> {
|
||||
fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) {
|
||||
match self {
|
||||
&Some(ref value) => (**value).to_jsval(cx, rval),
|
||||
&None => rval.set(NullValue()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<X: default::Default, T: FromJSValConvertible<Config=X>> FromJSValConvertible for Option<T> {
|
||||
type Config = ();
|
||||
fn from_jsval(cx: *mut JSContext, value: JSVal, _: ()) -> Result<Option<T>, ()> {
|
||||
if value.is_null_or_undefined() {
|
||||
fn from_jsval(cx: *mut JSContext, value: HandleValue, _: ()) -> Result<Option<T>, ()> {
|
||||
if value.get().is_null_or_undefined() {
|
||||
Ok(None)
|
||||
} else {
|
||||
let option: X = default::Default::default();
|
||||
|
@ -596,11 +645,10 @@ impl<X: default::Default, T: FromJSValConvertible<Config=X>> FromJSValConvertibl
|
|||
}
|
||||
|
||||
impl ToJSValConvertible for *mut JSObject {
|
||||
fn to_jsval(&self, cx: *mut JSContext) -> JSVal {
|
||||
let mut wrapped = ObjectOrNullValue(*self);
|
||||
fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) {
|
||||
rval.set(ObjectOrNullValue(*self));
|
||||
unsafe {
|
||||
assert!(JS_WrapValue(cx, &mut wrapped) != 0);
|
||||
assert!(JS_WrapValue(cx, rval) != 0);
|
||||
}
|
||||
wrapped
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,20 +6,22 @@
|
|||
|
||||
use dom::bindings::conversions::ToJSValConvertible;
|
||||
use dom::bindings::global::GlobalRef;
|
||||
use dom::bindings::js::Rootable;
|
||||
use dom::domexception::{DOMException, DOMErrorName};
|
||||
|
||||
use util::str::DOMString;
|
||||
|
||||
use js::jsapi::{JSContext, JSObject};
|
||||
use js::jsapi::{JSContext, JSObject, RootedValue};
|
||||
use js::jsapi::{JS_IsExceptionPending, JS_SetPendingException, JS_ReportPendingException};
|
||||
use js::jsapi::{JS_ReportErrorNumber, JSErrorFormatString, JSEXN_TYPEERR, JSEXN_RANGEERR};
|
||||
use js::jsapi::{JS_ReportErrorNumber1, JSErrorFormatString, JSExnType};
|
||||
use js::jsapi::{JS_SaveFrameChain, JS_RestoreFrameChain};
|
||||
use js::rust::with_compartment;
|
||||
use js::jsapi::JSAutoCompartment;
|
||||
use js::jsval::UndefinedValue;
|
||||
use js::JSFalse;
|
||||
|
||||
use libc;
|
||||
use std::ffi::CString;
|
||||
use std::ptr;
|
||||
use std::mem;
|
||||
|
||||
/// DOM exceptions that can be thrown by a native DOM method.
|
||||
#[derive(Debug, Clone)]
|
||||
|
@ -116,10 +118,11 @@ pub fn throw_dom_exception(cx: *mut JSContext, global: GlobalRef,
|
|||
};
|
||||
|
||||
assert!(unsafe { JS_IsExceptionPending(cx) } == 0);
|
||||
let exception = DOMException::new(global, code).root();
|
||||
let thrown = exception.to_jsval(cx);
|
||||
let exception = DOMException::new(global, code);
|
||||
let mut thrown = RootedValue::new(cx, UndefinedValue());
|
||||
exception.to_jsval(cx, thrown.handle_mut());
|
||||
unsafe {
|
||||
JS_SetPendingException(cx, thrown);
|
||||
JS_SetPendingException(cx, thrown.handle());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -128,9 +131,10 @@ pub fn report_pending_exception(cx: *mut JSContext, obj: *mut JSObject) {
|
|||
unsafe {
|
||||
if JS_IsExceptionPending(cx) != 0 {
|
||||
let saved = JS_SaveFrameChain(cx);
|
||||
with_compartment(cx, obj, || {
|
||||
{
|
||||
let _ac = JSAutoCompartment::new(cx, obj);
|
||||
JS_ReportPendingException(cx);
|
||||
});
|
||||
}
|
||||
if saved != 0 {
|
||||
JS_RestoreFrameChain(cx);
|
||||
}
|
||||
|
@ -158,25 +162,26 @@ static ERROR_FORMAT_STRING_STRING: [libc::c_char; 4] = [
|
|||
static mut TYPE_ERROR_FORMAT_STRING: JSErrorFormatString = JSErrorFormatString {
|
||||
format: &ERROR_FORMAT_STRING_STRING as *const libc::c_char,
|
||||
argCount: 1,
|
||||
exnType: JSEXN_TYPEERR as i16,
|
||||
exnType: JSExnType::JSEXN_TYPEERR as i16,
|
||||
};
|
||||
|
||||
/// Format string struct used to throw `RangeError`s.
|
||||
static mut RANGE_ERROR_FORMAT_STRING: JSErrorFormatString = JSErrorFormatString {
|
||||
format: &ERROR_FORMAT_STRING_STRING as *const libc::c_char,
|
||||
argCount: 1,
|
||||
exnType: JSEXN_RANGEERR as i16,
|
||||
exnType: JSExnType::JSEXN_RANGEERR as i16,
|
||||
};
|
||||
|
||||
/// Callback used to throw javascript errors.
|
||||
/// See throw_js_error for info about error_number.
|
||||
unsafe extern fn get_error_message(_user_ref: *mut libc::c_void,
|
||||
_locale: *const libc::c_char,
|
||||
error_number: libc::c_uint) -> *const JSErrorFormatString
|
||||
error_number: libc::c_uint)
|
||||
-> *const JSErrorFormatString
|
||||
{
|
||||
match error_number as i32 {
|
||||
JSEXN_TYPEERR => &TYPE_ERROR_FORMAT_STRING as *const JSErrorFormatString,
|
||||
JSEXN_RANGEERR => &RANGE_ERROR_FORMAT_STRING as *const JSErrorFormatString,
|
||||
let num: JSExnType = mem::transmute(error_number);
|
||||
match num {
|
||||
JSExnType::JSEXN_TYPEERR => &TYPE_ERROR_FORMAT_STRING as *const JSErrorFormatString,
|
||||
JSExnType::JSEXN_RANGEERR => &RANGE_ERROR_FORMAT_STRING as *const JSErrorFormatString,
|
||||
_ => panic!("Bad js error number given to get_error_message: {}", error_number)
|
||||
}
|
||||
}
|
||||
|
@ -188,20 +193,18 @@ unsafe extern fn get_error_message(_user_ref: *mut libc::c_void,
|
|||
fn throw_js_error(cx: *mut JSContext, error: &str, error_number: u32) {
|
||||
let error = CString::new(error).unwrap();
|
||||
unsafe {
|
||||
JS_ReportErrorNumber(cx,
|
||||
Some(get_error_message as
|
||||
unsafe extern "C" fn(*mut libc::c_void, *const libc::c_char,
|
||||
libc::c_uint) -> *const JSErrorFormatString),
|
||||
JS_ReportErrorNumber1(cx,
|
||||
Some(get_error_message),
|
||||
ptr::null_mut(), error_number, error.as_ptr());
|
||||
}
|
||||
}
|
||||
|
||||
/// Throw a `TypeError` with the given message.
|
||||
pub fn throw_type_error(cx: *mut JSContext, error: &str) {
|
||||
throw_js_error(cx, error, JSEXN_TYPEERR as u32);
|
||||
throw_js_error(cx, error, JSExnType::JSEXN_TYPEERR as u32);
|
||||
}
|
||||
|
||||
/// Throw a `RangeError` with the given message.
|
||||
pub fn throw_range_error(cx: *mut JSContext, error: &str) {
|
||||
throw_js_error(cx, error, JSEXN_RANGEERR as u32);
|
||||
throw_js_error(cx, error, JSExnType::JSEXN_RANGEERR as u32);
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
|
||||
use dom::bindings::conversions::native_from_reflector_jsmanaged;
|
||||
use dom::bindings::js::{JS, JSRef, Rootable, Root, Unrooted};
|
||||
use dom::bindings::js::{JS, Root};
|
||||
use dom::bindings::utils::{Reflectable, Reflector};
|
||||
use dom::document::DocumentHelpers;
|
||||
use dom::workerglobalscope::{WorkerGlobalScope, WorkerGlobalScopeHelpers};
|
||||
|
@ -21,7 +21,7 @@ use msg::constellation_msg::{PipelineId, WorkerId};
|
|||
use net_traits::ResourceTask;
|
||||
|
||||
use js::{JSCLASS_IS_GLOBAL, JSCLASS_IS_DOMJSCLASS};
|
||||
use js::glue::{GetGlobalForObjectCrossCompartment};
|
||||
use js::jsapi::{GetGlobalForObjectCrossCompartment};
|
||||
use js::jsapi::{JSContext, JSObject};
|
||||
use js::jsapi::{JS_GetClass};
|
||||
use url::Url;
|
||||
|
@ -30,9 +30,9 @@ use url::Url;
|
|||
#[derive(Copy, Clone)]
|
||||
pub enum GlobalRef<'a> {
|
||||
/// A reference to a `Window` object.
|
||||
Window(JSRef<'a, window::Window>),
|
||||
Window(&'a window::Window),
|
||||
/// A reference to a `WorkerGlobalScope` object.
|
||||
Worker(JSRef<'a, WorkerGlobalScope>),
|
||||
Worker(&'a WorkerGlobalScope),
|
||||
}
|
||||
|
||||
/// A stack-based rooted reference to a global object.
|
||||
|
@ -55,15 +55,6 @@ pub enum GlobalField {
|
|||
Worker(JS<WorkerGlobalScope>),
|
||||
}
|
||||
|
||||
/// An unrooted reference to a global object.
|
||||
#[must_root]
|
||||
pub enum GlobalUnrooted {
|
||||
/// An unrooted reference to a `Window` object.
|
||||
Window(Unrooted<window::Window>),
|
||||
/// An unrooted reference to a `WorkerGlobalScope` object.
|
||||
Worker(Unrooted<WorkerGlobalScope>),
|
||||
}
|
||||
|
||||
impl<'a> GlobalRef<'a> {
|
||||
/// Get the `JSContext` for the `JSRuntime` associated with the thread
|
||||
/// this global object is on.
|
||||
|
@ -76,7 +67,7 @@ impl<'a> GlobalRef<'a> {
|
|||
|
||||
/// Extract a `Window`, causing task failure if the global object is not
|
||||
/// a `Window`.
|
||||
pub fn as_window<'b>(&'b self) -> JSRef<'b, window::Window> {
|
||||
pub fn as_window<'b>(&'b self) -> &'b window::Window {
|
||||
match *self {
|
||||
GlobalRef::Window(window) => window,
|
||||
GlobalRef::Worker(_) => panic!("expected a Window scope"),
|
||||
|
@ -104,7 +95,7 @@ impl<'a> GlobalRef<'a> {
|
|||
pub fn resource_task(&self) -> ResourceTask {
|
||||
match *self {
|
||||
GlobalRef::Window(ref window) => {
|
||||
let doc = window.Document().root();
|
||||
let doc = window.Document();
|
||||
let doc = doc.r();
|
||||
let loader = doc.loader();
|
||||
loader.resource_task.clone()
|
||||
|
@ -182,8 +173,8 @@ impl GlobalField {
|
|||
/// Create a new `GlobalField` from a rooted reference.
|
||||
pub fn from_rooted(global: &GlobalRef) -> GlobalField {
|
||||
match *global {
|
||||
GlobalRef::Window(window) => GlobalField::Window(JS::from_rooted(window)),
|
||||
GlobalRef::Worker(worker) => GlobalField::Worker(JS::from_rooted(worker)),
|
||||
GlobalRef::Window(window) => GlobalField::Window(JS::from_ref(window)),
|
||||
GlobalRef::Worker(worker) => GlobalField::Worker(JS::from_ref(worker)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -196,30 +187,20 @@ impl GlobalField {
|
|||
}
|
||||
}
|
||||
|
||||
impl GlobalUnrooted {
|
||||
/// Create a stack-bounded root for this reference.
|
||||
pub fn root(&self) -> GlobalRoot {
|
||||
match *self {
|
||||
GlobalUnrooted::Window(ref window) => GlobalRoot::Window(window.root()),
|
||||
GlobalUnrooted::Worker(ref worker) => GlobalRoot::Worker(worker.root()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the global object of the realm that the given JS object was created in.
|
||||
#[allow(unrooted_must_root)]
|
||||
pub fn global_object_for_js_object(obj: *mut JSObject) -> GlobalUnrooted {
|
||||
pub fn global_object_for_js_object(obj: *mut JSObject) -> GlobalRoot {
|
||||
unsafe {
|
||||
let global = GetGlobalForObjectCrossCompartment(obj);
|
||||
let clasp = JS_GetClass(global);
|
||||
assert!(((*clasp).flags & (JSCLASS_IS_DOMJSCLASS | JSCLASS_IS_GLOBAL)) != 0);
|
||||
match native_from_reflector_jsmanaged(global) {
|
||||
Ok(window) => return GlobalUnrooted::Window(window),
|
||||
Ok(window) => return GlobalRoot::Window(window),
|
||||
Err(_) => (),
|
||||
}
|
||||
|
||||
match native_from_reflector_jsmanaged(global) {
|
||||
Ok(worker) => return GlobalUnrooted::Worker(worker),
|
||||
Ok(worker) => return GlobalRoot::Worker(worker),
|
||||
Err(_) => (),
|
||||
}
|
||||
|
||||
|
|
|
@ -11,178 +11,32 @@
|
|||
//!
|
||||
//! Here is a brief overview of the important types:
|
||||
//!
|
||||
//! - `JSRef<T>`: a freely-copyable reference to a rooted DOM object.
|
||||
//! - `Root<T>`: a stack-based reference to a rooted DOM object.
|
||||
//! - `JS<T>`: a reference to a DOM object that can automatically be traced by
|
||||
//! the GC when encountered as a field of a Rust structure.
|
||||
//! - `Temporary<T>`: a reference to a DOM object that will remain rooted for
|
||||
//! the duration of its lifetime.
|
||||
//!
|
||||
//! The rule of thumb is as follows:
|
||||
//! `JS<T>` does not allow access to their inner value without explicitly
|
||||
//! creating a stack-based root via the `root` method. This returns a `Root<T>`,
|
||||
//! which causes the JS-owned value to be uncollectable for the duration of the
|
||||
//! `Root` object's lifetime. A reference to the object can then be obtained
|
||||
//! from the `Root` object. These references are not allowed to outlive their
|
||||
//! originating `Root<T>`.
|
||||
//!
|
||||
//! - All methods return `Temporary<T>`, to ensure the value remains alive
|
||||
//! until it is stored somewhere that is reachable by the GC.
|
||||
//! - All functions take `JSRef<T>` arguments, to ensure that they will remain
|
||||
//! uncollected for the duration of their usage.
|
||||
//! - All DOM structs contain `JS<T>` fields and derive the `JSTraceable`
|
||||
//! trait, to ensure that they are transitively marked as reachable by the GC
|
||||
//! if the enclosing value is reachable.
|
||||
//! - All methods for type `T` are implemented for `JSRef<T>`, to ensure that
|
||||
//! the self value will not be collected for the duration of the method call.
|
||||
//!
|
||||
//! Both `Temporary<T>` and `JS<T>` do not allow access to their inner value
|
||||
//! without explicitly creating a stack-based root via the `root` method
|
||||
//! through the `Rootable<T>` trait. This returns a `Root<T>`, which causes the
|
||||
//! JS-owned value to be uncollectable for the duration of the `Root` object's
|
||||
//! lifetime. A `JSRef<T>` can be obtained from a `Root<T>` by calling the `r`
|
||||
//! method. These `JSRef<T>` values are not allowed to outlive their
|
||||
//! originating `Root<T>`, to ensure that all interactions with the enclosed
|
||||
//! value only occur when said value is uncollectable, and will cause static
|
||||
//! lifetime errors if misused.
|
||||
//!
|
||||
//! Other miscellaneous helper traits:
|
||||
//!
|
||||
//! - `OptionalRootable` and `OptionalOptionalRootable`: make rooting `Option`
|
||||
//! values easy via a `root` method
|
||||
//! - `ResultRootable`: make rooting successful `Result` values easy
|
||||
//! - `TemporaryPushable`: allows mutating vectors of `JS<T>` with new elements
|
||||
//! of `JSRef`/`Temporary`
|
||||
//! - `RootedReference`: makes obtaining an `Option<JSRef<T>>` from an
|
||||
//! `Option<Root<T>>` easy
|
||||
|
||||
use dom::bindings::trace::JSTraceable;
|
||||
use dom::bindings::trace::RootedVec;
|
||||
use dom::bindings::trace::trace_reflector;
|
||||
use dom::bindings::utils::{Reflector, Reflectable};
|
||||
use dom::node::Node;
|
||||
use js::jsapi::JSObject;
|
||||
use js::jsval::JSVal;
|
||||
use js::jsapi::{JSObject, Heap, JSTracer};
|
||||
use js::jsval::{JSVal, UndefinedValue};
|
||||
use layout_interface::TrustedNodeAddress;
|
||||
use script_task::STACK_ROOTS;
|
||||
|
||||
use core::nonzero::NonZero;
|
||||
use libc;
|
||||
use std::cell::{Cell, UnsafeCell};
|
||||
use std::default::Default;
|
||||
use std::intrinsics::return_address;
|
||||
use std::marker::PhantomData;
|
||||
use std::ops::Deref;
|
||||
|
||||
/// An unrooted, JS-owned value. Must not be held across a GC.
|
||||
///
|
||||
/// This is used in particular to wrap pointers extracted from a reflector.
|
||||
#[must_root]
|
||||
pub struct Unrooted<T> {
|
||||
ptr: NonZero<*const T>
|
||||
}
|
||||
|
||||
impl<T: Reflectable> Unrooted<T> {
|
||||
/// Create a new JS-owned value wrapped from a raw Rust pointer.
|
||||
pub unsafe fn from_raw(raw: *const T) -> Unrooted<T> {
|
||||
assert!(!raw.is_null());
|
||||
Unrooted {
|
||||
ptr: NonZero::new(raw)
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new unrooted value from a `JS<T>`.
|
||||
#[allow(unrooted_must_root)]
|
||||
pub fn from_js(ptr: JS<T>) -> Unrooted<T> {
|
||||
Unrooted {
|
||||
ptr: ptr.ptr
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new unrooted value from a `Temporary<T>`.
|
||||
#[allow(unrooted_must_root)]
|
||||
pub fn from_temporary(ptr: Temporary<T>) -> Unrooted<T> {
|
||||
Unrooted::from_js(ptr.inner)
|
||||
}
|
||||
|
||||
/// Get the `Reflector` for this pointer.
|
||||
pub fn reflector<'a>(&'a self) -> &'a Reflector {
|
||||
unsafe {
|
||||
(**self.ptr).reflector()
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an unsafe pointer to the interior of this object.
|
||||
pub unsafe fn unsafe_get(&self) -> *const T {
|
||||
*self.ptr
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Reflectable> Rootable<T> for Unrooted<T> {
|
||||
/// Create a stack-bounded root for this value.
|
||||
fn root(&self) -> Root<T> {
|
||||
STACK_ROOTS.with(|ref collection| {
|
||||
let RootCollectionPtr(collection) = collection.get().unwrap();
|
||||
unsafe {
|
||||
Root::new(&*collection, self.ptr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Copy for Unrooted<T> {}
|
||||
impl<T> Clone for Unrooted<T> {
|
||||
fn clone(&self) -> Unrooted<T> { *self }
|
||||
}
|
||||
|
||||
/// A type that represents a JS-owned value that is rooted for the lifetime of
|
||||
/// this value. Importantly, it requires explicit rooting in order to interact
|
||||
/// with the inner value. Can be assigned into JS-owned member fields (i.e.
|
||||
/// `JS<T>` types) safely via the `JS<T>::assign` method or
|
||||
/// `OptionalSettable::assign` (for `Option<JS<T>>` fields).
|
||||
#[allow(unrooted_must_root)]
|
||||
pub struct Temporary<T> {
|
||||
inner: JS<T>,
|
||||
/// On-stack JS pointer to assuage conservative stack scanner
|
||||
_js_ptr: *mut JSObject,
|
||||
}
|
||||
|
||||
impl<T> Clone for Temporary<T> {
|
||||
fn clone(&self) -> Temporary<T> {
|
||||
Temporary {
|
||||
inner: self.inner,
|
||||
_js_ptr: self._js_ptr,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> PartialEq for Temporary<T> {
|
||||
fn eq(&self, other: &Temporary<T>) -> bool {
|
||||
self.inner == other.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Reflectable> Temporary<T> {
|
||||
/// Create a new `Temporary` value from an unrooted value.
|
||||
#[allow(unrooted_must_root)]
|
||||
pub fn from_unrooted(unrooted: Unrooted<T>) -> Temporary<T> {
|
||||
Temporary {
|
||||
inner: JS { ptr: unrooted.ptr },
|
||||
_js_ptr: unrooted.reflector().get_jsobject(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new `Temporary` value from a rooted value.
|
||||
#[allow(unrooted_must_root)]
|
||||
pub fn from_rooted<U: Assignable<T>>(root: U) -> Temporary<T> {
|
||||
let inner = JS::from_rooted(root);
|
||||
Temporary {
|
||||
inner: inner,
|
||||
_js_ptr: inner.reflector().get_jsobject(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Reflectable> Rootable<T> for Temporary<T> {
|
||||
/// Create a stack-bounded root for this value.
|
||||
fn root(&self) -> Root<T> {
|
||||
self.inner.root()
|
||||
}
|
||||
}
|
||||
|
||||
/// A traced reference to a DOM object. Must only be used as a field in other
|
||||
/// DOM objects.
|
||||
#[must_root]
|
||||
|
@ -198,6 +52,32 @@ impl<T> JS<T> {
|
|||
}
|
||||
}
|
||||
}
|
||||
impl<T: Reflectable> JS<T> {
|
||||
/// Root this JS-owned value to prevent its collection as garbage.
|
||||
pub fn root(&self) -> Root<T> {
|
||||
Root::new(self.ptr)
|
||||
}
|
||||
/// Create a JS<T> from a Root<T>
|
||||
/// XXX Not a great API. Should be a call on Root<T> instead
|
||||
pub fn from_rooted(root: &Root<T>) -> JS<T> {
|
||||
JS {
|
||||
ptr: unsafe { NonZero::new(&**root) }
|
||||
}
|
||||
}
|
||||
/// Create a JS<T> from a &T
|
||||
pub fn from_ref(obj: &T) -> JS<T> {
|
||||
JS {
|
||||
ptr: unsafe { NonZero::new(&*obj) }
|
||||
}
|
||||
}
|
||||
/// Store an rooted value in this field. This is safe under the
|
||||
/// assumption that JS<T> values are only used as fields in DOM types that
|
||||
/// are reachable in the GC graph, so this unrooted value becomes
|
||||
/// transitively rooted for the lifetime of its new owner.
|
||||
pub fn assign(&mut self, val: Root<T>) {
|
||||
self.ptr = val.ptr.clone();
|
||||
}
|
||||
}
|
||||
|
||||
/// An unrooted reference to a DOM object for use in layout. `Layout*Helpers`
|
||||
/// traits must be implemented on this.
|
||||
|
@ -208,7 +88,7 @@ pub struct LayoutJS<T> {
|
|||
impl<T: Reflectable> LayoutJS<T> {
|
||||
/// Get the reflector.
|
||||
pub unsafe fn get_jsobject(&self) -> *mut JSObject {
|
||||
(**self.ptr).reflector().get_jsobject()
|
||||
(**self.ptr).reflector().get_jsobject().get()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -217,14 +97,12 @@ impl<T> Copy for JS<T> {}
|
|||
impl<T> Copy for LayoutJS<T> {}
|
||||
|
||||
impl<T> PartialEq for JS<T> {
|
||||
#[allow(unrooted_must_root)]
|
||||
fn eq(&self, other: &JS<T>) -> bool {
|
||||
self.ptr == other.ptr
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> PartialEq for LayoutJS<T> {
|
||||
#[allow(unrooted_must_root)]
|
||||
fn eq(&self, other: &LayoutJS<T>) -> bool {
|
||||
self.ptr == other.ptr
|
||||
}
|
||||
|
@ -260,29 +138,6 @@ impl LayoutJS<Node> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Reflectable> Rootable<T> for JS<T> {
|
||||
/// Root this JS-owned value to prevent its collection as garbage.
|
||||
fn root(&self) -> Root<T> {
|
||||
STACK_ROOTS.with(|ref collection| {
|
||||
let RootCollectionPtr(collection) = collection.get().unwrap();
|
||||
unsafe {
|
||||
Root::new(&*collection, self.ptr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<U: Reflectable> JS<U> {
|
||||
/// Create a `JS<T>` from any JS-managed pointer.
|
||||
pub fn from_rooted<T: Assignable<U>>(root: T) -> JS<U> {
|
||||
unsafe {
|
||||
root.get_js()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//XXXjdm This is disappointing. This only gets called from trace hooks, in theory,
|
||||
// so it's safe to assume that self is rooted and thereby safe to access.
|
||||
impl<T: Reflectable> Reflectable for JS<T> {
|
||||
fn reflector<'a>(&'a self) -> &'a Reflector {
|
||||
unsafe {
|
||||
|
@ -298,19 +153,50 @@ impl<T: Reflectable> Reflectable for JS<T> {
|
|||
pub trait HeapGCValue: JSTraceable {
|
||||
}
|
||||
|
||||
impl HeapGCValue for JSVal {
|
||||
impl HeapGCValue for Heap<JSVal> {
|
||||
}
|
||||
|
||||
impl<T: Reflectable> HeapGCValue for JS<T> {
|
||||
}
|
||||
|
||||
/// A holder that provides interior mutability for GC-managed values such as
|
||||
/// `JSVal` and `JS<T>`.
|
||||
/// A holder that provides interior mutability for GC-managed JSVals.
|
||||
///
|
||||
/// Must be used in place of traditional interior mutability to ensure proper
|
||||
/// GC barriers are enforced.
|
||||
#[must_root]
|
||||
#[jstraceable]
|
||||
pub struct MutHeapJSVal {
|
||||
val: UnsafeCell<Heap<JSVal>>,
|
||||
}
|
||||
|
||||
impl MutHeapJSVal {
|
||||
/// Create a new `MutHeapJSVal`.
|
||||
pub fn new() -> MutHeapJSVal {
|
||||
MutHeapJSVal {
|
||||
val: UnsafeCell::new(Heap::default()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Set this `MutHeapJSVal` to the given value, calling write barriers as
|
||||
/// appropriate.
|
||||
pub fn set(&self, val: JSVal) {
|
||||
unsafe {
|
||||
let cell = self.val.get();
|
||||
(*cell).set(val);
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the value in this `MutHeapJSVal`, calling read barriers as appropriate.
|
||||
pub fn get(&self) -> JSVal {
|
||||
unsafe { (*self.val.get()).get() }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// A holder that provides interior mutability for GC-managed values such as
|
||||
/// `JS<T>`.
|
||||
#[must_root]
|
||||
#[jstraceable]
|
||||
pub struct MutHeap<T: HeapGCValue+Copy> {
|
||||
val: Cell<T>,
|
||||
}
|
||||
|
@ -323,13 +209,12 @@ impl<T: HeapGCValue+Copy> MutHeap<T> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Set this `MutHeap` to the given value, calling write barriers as
|
||||
/// appropriate.
|
||||
/// Set this `MutHeap` to the given value.
|
||||
pub fn set(&self, val: T) {
|
||||
self.val.set(val)
|
||||
}
|
||||
|
||||
/// Set the value in this `MutHeap`, calling read barriers as appropriate.
|
||||
/// Set the value in this `MutHeap`.
|
||||
pub fn get(&self) -> T {
|
||||
self.val.get()
|
||||
}
|
||||
|
@ -353,8 +238,7 @@ impl<T: HeapGCValue+Copy> MutNullableHeap<T> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Set this `MutNullableHeap` to the given value, calling write barriers
|
||||
/// as appropriate.
|
||||
/// Set this `MutNullableHeap` to the given value.
|
||||
pub fn set(&self, val: Option<T>) {
|
||||
self.ptr.set(val);
|
||||
}
|
||||
|
@ -368,14 +252,14 @@ impl<T: HeapGCValue+Copy> MutNullableHeap<T> {
|
|||
impl<T: Reflectable> MutNullableHeap<JS<T>> {
|
||||
/// Retrieve a copy of the current inner value. If it is `None`, it is
|
||||
/// initialized with the result of `cb` first.
|
||||
pub fn or_init<F>(&self, cb: F) -> Temporary<T>
|
||||
where F: FnOnce() -> Temporary<T>
|
||||
pub fn or_init<F>(&self, cb: F) -> Root<T>
|
||||
where F: FnOnce() -> Root<T>
|
||||
{
|
||||
match self.get() {
|
||||
Some(inner) => Temporary::from_rooted(inner),
|
||||
Some(inner) => Root::from_rooted(inner),
|
||||
None => {
|
||||
let inner = cb();
|
||||
self.set(Some(JS::from_rooted(inner.clone())));
|
||||
self.set(Some(JS::from_rooted(&inner)));
|
||||
inner
|
||||
},
|
||||
}
|
||||
|
@ -396,16 +280,6 @@ impl<T: HeapGCValue+Copy> Default for MutNullableHeap<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Reflectable> JS<T> {
|
||||
/// Store an unrooted value in this field. This is safe under the
|
||||
/// assumption that JS<T> values are only used as fields in DOM types that
|
||||
/// are reachable in the GC graph, so this unrooted value becomes
|
||||
/// transitively rooted for the lifetime of its new owner.
|
||||
pub fn assign(&mut self, val: Temporary<T>) {
|
||||
*self = val.inner.clone();
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Reflectable> LayoutJS<T> {
|
||||
/// Returns an unsafe pointer to the interior of this JS object. This is
|
||||
/// the only method that be safely accessed from layout. (The fact that
|
||||
|
@ -419,129 +293,36 @@ impl<T: Reflectable> LayoutJS<T> {
|
|||
pub trait RootedReference<T> {
|
||||
/// Obtain a safe optional reference to the wrapped JS owned-value that
|
||||
/// cannot outlive the lifetime of this root.
|
||||
fn r<'a>(&'a self) -> Option<JSRef<'a, T>>;
|
||||
fn r<'a>(&'a self) -> Option<&'a T>;
|
||||
}
|
||||
|
||||
impl<T: Reflectable> RootedReference<T> for Option<Root<T>> {
|
||||
fn r<'a>(&'a self) -> Option<JSRef<'a, T>> {
|
||||
fn r<'a>(&'a self) -> Option<&'a T> {
|
||||
self.as_ref().map(|root| root.r())
|
||||
}
|
||||
}
|
||||
|
||||
/// Get an `Option<Option<JSRef<T>>>` out of an `Option<Option<Root<T>>>`
|
||||
/// Get an `Option<Option<&T>>` out of an `Option<Option<Root<T>>>`
|
||||
pub trait OptionalRootedReference<T> {
|
||||
/// Obtain a safe optional optional reference to the wrapped JS owned-value
|
||||
/// that cannot outlive the lifetime of this root.
|
||||
fn r<'a>(&'a self) -> Option<Option<JSRef<'a, T>>>;
|
||||
fn r<'a>(&'a self) -> Option<Option<&'a T>>;
|
||||
}
|
||||
|
||||
impl<T: Reflectable> OptionalRootedReference<T> for Option<Option<Root<T>>> {
|
||||
fn r<'a>(&'a self) -> Option<Option<JSRef<'a, T>>> {
|
||||
fn r<'a>(&'a self) -> Option<Option<&'a T>> {
|
||||
self.as_ref().map(|inner| inner.r())
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait that allows extracting a `JS<T>` value from a variety of
|
||||
/// rooting-related containers, which in general is an unsafe operation since
|
||||
/// they can outlive the rooted lifetime of the original value.
|
||||
pub trait Assignable<T> {
|
||||
/// Extract an unrooted `JS<T>`.
|
||||
unsafe fn get_js(&self) -> JS<T>;
|
||||
}
|
||||
|
||||
impl<T> Assignable<T> for JS<T> {
|
||||
unsafe fn get_js(&self) -> JS<T> {
|
||||
self.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Reflectable> Assignable<T> for JSRef<'a, T> {
|
||||
unsafe fn get_js(&self) -> JS<T> {
|
||||
JS {
|
||||
ptr: self.ptr
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Reflectable> Assignable<T> for Temporary<T> {
|
||||
unsafe fn get_js(&self) -> JS<T> {
|
||||
self.inner.clone()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Root a rootable `Option` type (used for `Option<Temporary<T>>`)
|
||||
pub trait OptionalRootable<T> {
|
||||
/// Root the inner value, if it exists.
|
||||
fn root(&self) -> Option<Root<T>>;
|
||||
}
|
||||
|
||||
impl<T: Reflectable, U: Rootable<T>> OptionalRootable<T> for Option<U> {
|
||||
fn root(&self) -> Option<Root<T>> {
|
||||
self.as_ref().map(|inner| inner.root())
|
||||
}
|
||||
}
|
||||
|
||||
/// Root a rootable `Option<Option>` type (used for `Option<Option<JS<T>>>`)
|
||||
pub trait OptionalOptionalRootable<T> {
|
||||
/// Root the inner value, if it exists.
|
||||
fn root(&self) -> Option<Option<Root<T>>>;
|
||||
}
|
||||
|
||||
impl<T: Reflectable, U: OptionalRootable<T>> OptionalOptionalRootable<T> for Option<U> {
|
||||
fn root(&self) -> Option<Option<Root<T>>> {
|
||||
self.as_ref().map(|inner| inner.root())
|
||||
}
|
||||
}
|
||||
|
||||
/// Root a rootable `Result` type (any of `Temporary<T>` or `JS<T>`)
|
||||
pub trait ResultRootable<T,U> {
|
||||
/// Root the inner value, if it exists.
|
||||
fn root(self) -> Result<Root<T>, U>;
|
||||
}
|
||||
|
||||
impl<T: Reflectable, U, V: Rootable<T>> ResultRootable<T, U> for Result<V, U> {
|
||||
fn root(self) -> Result<Root<T>, U> {
|
||||
self.map(|inner| inner.root())
|
||||
}
|
||||
}
|
||||
|
||||
/// Root a rootable type.
|
||||
pub trait Rootable<T> {
|
||||
/// Root the value.
|
||||
fn root(&self) -> Root<T>;
|
||||
}
|
||||
|
||||
|
||||
/// Provides a facility to push unrooted values onto lists of rooted values.
|
||||
/// This is safe under the assumption that said lists are reachable via the GC
|
||||
/// graph, and therefore the new values are transitively rooted for the
|
||||
/// lifetime of their new owner.
|
||||
pub trait TemporaryPushable<T> {
|
||||
/// Push a new value onto this container.
|
||||
fn push_unrooted(&mut self, val: &T);
|
||||
/// Insert a new value into this container.
|
||||
fn insert_unrooted(&mut self, index: usize, val: &T);
|
||||
}
|
||||
|
||||
impl<T: Assignable<U>, U: Reflectable> TemporaryPushable<T> for Vec<JS<U>> {
|
||||
fn push_unrooted(&mut self, val: &T) {
|
||||
self.push(unsafe { val.get_js() });
|
||||
}
|
||||
|
||||
fn insert_unrooted(&mut self, index: usize, val: &T) {
|
||||
self.insert(index, unsafe { val.get_js() });
|
||||
}
|
||||
}
|
||||
|
||||
/// An opaque, LIFO rooting mechanism. This tracks roots and ensures that they
|
||||
/// are destructed in a LIFO order.
|
||||
/// A rooting mechanism for reflectors on the stack.
|
||||
/// LIFO is not required.
|
||||
///
|
||||
/// See also [*Exact Stack Rooting - Storing a GCPointer on the CStack*]
|
||||
/// (https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/Internals/GC/Exact_Stack_Rooting).
|
||||
#[no_move]
|
||||
pub struct RootCollection {
|
||||
roots: UnsafeCell<RootedVec<*mut JSObject>>,
|
||||
roots: UnsafeCell<Vec<*const Reflector>>,
|
||||
}
|
||||
|
||||
/// A pointer to a RootCollection, for use in global variables.
|
||||
|
@ -555,142 +336,114 @@ impl Clone for RootCollectionPtr {
|
|||
impl RootCollection {
|
||||
/// Create an empty collection of roots
|
||||
pub fn new() -> RootCollection {
|
||||
let addr = unsafe {
|
||||
return_address() as *const libc::c_void
|
||||
};
|
||||
|
||||
RootCollection {
|
||||
roots: UnsafeCell::new(RootedVec::new_with_destination_address(addr)),
|
||||
roots: UnsafeCell::new(vec!()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Track a stack-based root as a pointer to ensure LIFO root ordering.
|
||||
fn root<'b>(&self, untracked_js_ptr: *mut JSObject) {
|
||||
/// Start tracking a stack-based root
|
||||
fn root<'b>(&self, untracked_reflector: *const Reflector) {
|
||||
unsafe {
|
||||
let roots = self.roots.get();
|
||||
(*roots).push(untracked_js_ptr);
|
||||
debug!(" rooting {:?}", untracked_js_ptr);
|
||||
let mut roots = &mut *self.roots.get();
|
||||
roots.push(untracked_reflector);
|
||||
assert!(!(*untracked_reflector).get_jsobject().is_null())
|
||||
}
|
||||
}
|
||||
|
||||
/// Stop tracking a stack-based root, asserting if LIFO root ordering has
|
||||
/// been violated
|
||||
/// Stop tracking a stack-based root, asserting if the reflector isn't found
|
||||
fn unroot<'b, T: Reflectable>(&self, rooted: &Root<T>) {
|
||||
unsafe {
|
||||
let roots = self.roots.get();
|
||||
let unrooted = (*roots).pop().unwrap();
|
||||
debug!("unrooted {:?} (expecting {:?}", unrooted, rooted.js_ptr);
|
||||
assert!(unrooted == rooted.js_ptr);
|
||||
let mut roots = &mut *self.roots.get();
|
||||
let old_reflector = &*rooted.r().reflector();
|
||||
match roots.iter().rposition(|r| *r == old_reflector) {
|
||||
Some(idx) => {
|
||||
roots.remove(idx);
|
||||
},
|
||||
None => panic!("Can't remove a root that was never rooted!")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// SM Callback that traces the rooted reflectors
|
||||
pub unsafe fn trace_roots(tracer: *mut JSTracer) {
|
||||
STACK_ROOTS.with(|ref collection| {
|
||||
let RootCollectionPtr(collection) = collection.get().unwrap();
|
||||
let collection = &*(*collection).roots.get();
|
||||
for root in collection.iter() {
|
||||
trace_reflector(tracer, "reflector", &**root);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// A rooted reference to a DOM object.
|
||||
///
|
||||
/// The JS value is pinned for the duration of this object's lifetime; roots
|
||||
/// are additive, so this object's destruction will not invalidate other roots
|
||||
/// for the same JS value. `Root`s cannot outlive the associated
|
||||
/// `RootCollection` object. Attempts to transfer ownership of a `Root` via
|
||||
/// moving will trigger dynamic unrooting failures due to incorrect ordering.
|
||||
#[no_move]
|
||||
/// `RootCollection` object.
|
||||
pub struct Root<T: Reflectable> {
|
||||
/// List that ensures correct dynamic root ordering
|
||||
root_list: &'static RootCollection,
|
||||
/// Reference to rooted value that must not outlive this container
|
||||
ptr: NonZero<*const T>,
|
||||
/// On-stack JS pointer to assuage conservative stack scanner
|
||||
js_ptr: *mut JSObject,
|
||||
/// List that ensures correct dynamic root ordering
|
||||
root_list: *const RootCollection,
|
||||
}
|
||||
|
||||
impl<T: Reflectable> Root<T> {
|
||||
/// Create a new stack-bounded root for the provided JS-owned value.
|
||||
/// It cannot not outlive its associated `RootCollection`, and it contains
|
||||
/// a `JSRef` which cannot outlive this new `Root`.
|
||||
#[inline]
|
||||
fn new(roots: &'static RootCollection, unrooted: NonZero<*const T>)
|
||||
-> Root<T> {
|
||||
let js_ptr = unsafe {
|
||||
(**unrooted).reflector().get_jsobject()
|
||||
};
|
||||
roots.root(js_ptr);
|
||||
Root {
|
||||
root_list: roots,
|
||||
ptr: unrooted,
|
||||
js_ptr: js_ptr,
|
||||
}
|
||||
/// It cannot not outlive its associated `RootCollection`, and it gives
|
||||
/// out references which cannot outlive this new `Root`.
|
||||
pub fn new(unrooted: NonZero<*const T>)
|
||||
-> Root<T> {
|
||||
STACK_ROOTS.with(|ref collection| {
|
||||
let RootCollectionPtr(collection) = collection.get().unwrap();
|
||||
unsafe { (*collection).root(&*(**unrooted).reflector()) }
|
||||
Root {
|
||||
ptr: unrooted,
|
||||
root_list: collection,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Generate a new root from a reference
|
||||
pub fn from_ref(unrooted: &T) -> Root<T> {
|
||||
Root::new(unsafe { NonZero::new(&*unrooted) })
|
||||
}
|
||||
|
||||
/// Obtain a safe reference to the wrapped JS owned-value that cannot
|
||||
/// outlive the lifetime of this root.
|
||||
pub fn r<'b>(&'b self) -> JSRef<'b, T> {
|
||||
JSRef {
|
||||
ptr: self.ptr,
|
||||
chain: PhantomData,
|
||||
}
|
||||
pub fn r<'a>(&'a self) -> &'a T {
|
||||
&**self
|
||||
}
|
||||
|
||||
/// Obtain an unsafe reference to the wrapped JS owned-value that can
|
||||
/// outlive the lifetime of this root.
|
||||
///
|
||||
/// DO NOT CALL.
|
||||
pub fn get_unsound_ref_forever<'b>(&self) -> JSRef<'b, T> {
|
||||
JSRef {
|
||||
ptr: self.ptr,
|
||||
chain: PhantomData,
|
||||
}
|
||||
/// Don't use this. Don't make me find you.
|
||||
pub fn get_unsound_ref_forever<'a, 'b>(&'a self) -> &'b T {
|
||||
unsafe { &**self.ptr }
|
||||
}
|
||||
|
||||
/// Generate a new root from a JS<T> reference
|
||||
#[allow(unrooted_must_root)]
|
||||
pub fn from_rooted(js: JS<T>) -> Root<T> {
|
||||
js.root()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Reflectable> Deref for Root<T> {
|
||||
type Target = T;
|
||||
fn deref<'a>(&'a self) -> &'a T {
|
||||
unsafe { &**self.ptr.deref() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Reflectable> PartialEq for Root<T> {
|
||||
fn eq(&self, other: &Root<T>) -> bool {
|
||||
self.ptr == other.ptr
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Reflectable> Drop for Root<T> {
|
||||
fn drop(&mut self) {
|
||||
self.root_list.unroot(self);
|
||||
unsafe { (*self.root_list).unroot(self); }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Reflectable> Deref for JSRef<'a, T> {
|
||||
type Target = T;
|
||||
fn deref<'b>(&'b self) -> &'b T {
|
||||
unsafe {
|
||||
&**self.ptr
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A reference to a DOM object that is guaranteed to be alive. This is freely
|
||||
/// copyable.
|
||||
pub struct JSRef<'a, T> {
|
||||
ptr: NonZero<*const T>,
|
||||
chain: PhantomData<&'a ()>,
|
||||
}
|
||||
|
||||
impl<'a, T> Copy for JSRef<'a, T> {}
|
||||
|
||||
impl<'a, T> Clone for JSRef<'a, T> {
|
||||
fn clone(&self) -> JSRef<'a, T> {
|
||||
JSRef {
|
||||
ptr: self.ptr.clone(),
|
||||
chain: self.chain,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b, T> PartialEq<JSRef<'b, T>> for JSRef<'a, T> {
|
||||
fn eq(&self, other: &JSRef<T>) -> bool {
|
||||
self.ptr == other.ptr
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Reflectable> JSRef<'a, T> {
|
||||
/// Returns the inner pointer directly.
|
||||
pub fn extended_deref(self) -> &'a T {
|
||||
unsafe {
|
||||
&**self.ptr
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Reflectable> Reflectable for JSRef<'a, T> {
|
||||
fn reflector<'b>(&'b self) -> &'b Reflector {
|
||||
(**self).reflector()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,18 +8,19 @@
|
|||
|
||||
use dom::bindings::conversions::is_dom_proxy;
|
||||
use dom::bindings::utils::delete_property_by_id;
|
||||
use js::jsapi::{JSContext, jsid, JSPropertyDescriptor, JSObject, JSString};
|
||||
use js::jsapi::{JSContext, JSPropertyDescriptor, JSObject, JSString};
|
||||
use js::jsapi::{JS_GetPropertyDescriptorById, JS_NewStringCopyN};
|
||||
use js::jsapi::{JS_DefinePropertyById, JS_NewObjectWithGivenProto};
|
||||
use js::jsapi::{JS_ReportErrorFlagsAndNumber, JS_StrictPropertyStub};
|
||||
use js::jsapi::{JSREPORT_WARNING, JSREPORT_STRICT, JSREPORT_STRICT_MODE_ERROR};
|
||||
use js::jsapi::{JS_DefinePropertyById6, JS_NewObjectWithGivenProto};
|
||||
use js::jsapi::{JS_StrictPropertyStub, JSErrNum};
|
||||
use js::jsapi::{Handle, HandleObject, HandleId, MutableHandle, RootedObject, ObjectOpResult};
|
||||
use js::jsapi::AutoIdVector;
|
||||
use js::jsapi::GetObjectProto;
|
||||
use js::jsval::ObjectValue;
|
||||
use js::glue::GetProxyExtra;
|
||||
use js::glue::{GetObjectProto, GetObjectParent, SetProxyExtra, GetProxyHandler};
|
||||
use js::glue::{SetProxyExtra, GetProxyHandler};
|
||||
use js::glue::InvokeGetOwnPropertyDescriptor;
|
||||
use js::glue::RUST_js_GetErrorMessage;
|
||||
use js::glue::AutoIdVector;
|
||||
use js::{JSPROP_GETTER, JSPROP_ENUMERATE, JSPROP_READONLY, JSRESOLVE_QUALIFIED};
|
||||
use js::{JSPROP_GETTER, JSPROP_ENUMERATE, JSPROP_READONLY};
|
||||
use js::{JSTrue, JSFalse};
|
||||
|
||||
use libc;
|
||||
use std::mem;
|
||||
|
@ -32,60 +33,78 @@ static JSPROXYSLOT_EXPANDO: u32 = 0;
|
|||
/// Otherwise, walk along the prototype chain to find a property with that
|
||||
/// name.
|
||||
pub unsafe extern fn get_property_descriptor(cx: *mut JSContext,
|
||||
proxy: *mut JSObject,
|
||||
id: jsid, set: bool,
|
||||
desc: *mut JSPropertyDescriptor)
|
||||
-> bool {
|
||||
let handler = GetProxyHandler(proxy);
|
||||
if !InvokeGetOwnPropertyDescriptor(handler, cx, proxy, id, set, desc) {
|
||||
return false;
|
||||
proxy: HandleObject,
|
||||
id: HandleId,
|
||||
desc: MutableHandle<JSPropertyDescriptor>)
|
||||
-> u8 {
|
||||
let handler = GetProxyHandler(proxy.get());
|
||||
if InvokeGetOwnPropertyDescriptor(handler, cx, proxy, id, desc) == 0 {
|
||||
return JSFalse;
|
||||
}
|
||||
if !(*desc).obj.is_null() {
|
||||
return true;
|
||||
if !desc.get().obj.is_null() {
|
||||
return JSTrue;
|
||||
}
|
||||
|
||||
//let proto = JS_GetPrototype(proxy);
|
||||
let proto = GetObjectProto(proxy);
|
||||
if proto.is_null() {
|
||||
(*desc).obj = ptr::null_mut();
|
||||
return true;
|
||||
let mut proto = RootedObject::new(cx, ptr::null_mut());
|
||||
if GetObjectProto(cx, proxy, proto.handle_mut()) == 0 {
|
||||
desc.get().obj = ptr::null_mut();
|
||||
return JSTrue;
|
||||
}
|
||||
|
||||
JS_GetPropertyDescriptorById(cx, proto, id, JSRESOLVE_QUALIFIED, desc) != 0
|
||||
JS_GetPropertyDescriptorById(cx, proto.handle(), id, desc)
|
||||
}
|
||||
|
||||
/// Defines an expando on the given `proxy`.
|
||||
pub unsafe extern fn define_property(cx: *mut JSContext, proxy: *mut JSObject,
|
||||
id: jsid, desc: *mut JSPropertyDescriptor)
|
||||
-> bool {
|
||||
static JSMSG_GETTER_ONLY: libc::c_uint = 160;
|
||||
|
||||
pub unsafe extern fn define_property(cx: *mut JSContext, proxy: HandleObject,
|
||||
id: HandleId, desc: Handle<JSPropertyDescriptor>,
|
||||
result: *mut ObjectOpResult)
|
||||
-> u8 {
|
||||
//FIXME: Workaround for https://github.com/mozilla/rust/issues/13385
|
||||
let setter: *const libc::c_void = mem::transmute((*desc).setter);
|
||||
let setter: *const libc::c_void = mem::transmute(desc.get().setter);
|
||||
let setter_stub: *const libc::c_void = mem::transmute(JS_StrictPropertyStub);
|
||||
if ((*desc).attrs & JSPROP_GETTER) != 0 && setter == setter_stub {
|
||||
return JS_ReportErrorFlagsAndNumber(cx,
|
||||
JSREPORT_WARNING | JSREPORT_STRICT |
|
||||
JSREPORT_STRICT_MODE_ERROR,
|
||||
Some(RUST_js_GetErrorMessage), ptr::null_mut(),
|
||||
JSMSG_GETTER_ONLY) != 0;
|
||||
if (desc.get().attrs & JSPROP_GETTER) != 0 && setter == setter_stub {
|
||||
(*result).code_ = JSErrNum::JSMSG_GETTER_ONLY as u32;
|
||||
return JSTrue;
|
||||
}
|
||||
|
||||
let expando = ensure_expando_object(cx, proxy);
|
||||
return JS_DefinePropertyById(cx, expando, id, (*desc).value, (*desc).getter,
|
||||
(*desc).setter, (*desc).attrs) != 0;
|
||||
let expando = RootedObject::new(cx, ensure_expando_object(cx, proxy));
|
||||
JS_DefinePropertyById6(cx, expando.handle(), id, desc, result)
|
||||
}
|
||||
|
||||
/// Deletes an expando off the given `proxy`.
|
||||
pub unsafe extern fn delete(cx: *mut JSContext, proxy: *mut JSObject, id: jsid,
|
||||
bp: *mut bool) -> bool {
|
||||
let expando = get_expando_object(proxy);
|
||||
if expando.is_null() {
|
||||
*bp = true;
|
||||
return true;
|
||||
pub unsafe extern fn delete(cx: *mut JSContext, proxy: HandleObject, id: HandleId,
|
||||
bp: *mut ObjectOpResult) -> u8 {
|
||||
let expando = RootedObject::new(cx, get_expando_object(proxy));
|
||||
if expando.ptr.is_null() {
|
||||
(*bp).code_ = 0 /* OkCode */;
|
||||
return JSTrue;
|
||||
}
|
||||
|
||||
return delete_property_by_id(cx, expando, id, &mut *bp);
|
||||
delete_property_by_id(cx, expando.handle(), id, bp)
|
||||
}
|
||||
|
||||
/// Stub for ownPropertyKeys
|
||||
pub unsafe extern fn own_property_keys(cx: *mut JSContext,
|
||||
proxy: HandleObject,
|
||||
props: *mut AutoIdVector) -> u8 {
|
||||
// FIXME: implement this
|
||||
// https://github.com/servo/servo/issues/6390
|
||||
JSTrue
|
||||
}
|
||||
|
||||
/// Controls whether the Extensible bit can be changed
|
||||
pub unsafe extern fn prevent_extensions(_cx: *mut JSContext,
|
||||
_proxy: HandleObject,
|
||||
result: *mut ObjectOpResult) -> u8 {
|
||||
(*result).code_ = JSErrNum::JSMSG_CANT_PREVENT_EXTENSIONS as u32;
|
||||
return JSTrue;
|
||||
}
|
||||
|
||||
/// Reports whether the object is Extensible
|
||||
pub unsafe extern fn is_extensible(_cx: *mut JSContext, _proxy: HandleObject,
|
||||
succeeded: *mut u8) -> u8 {
|
||||
*succeeded = JSTrue;
|
||||
return JSTrue;
|
||||
}
|
||||
|
||||
/// Returns the stringification of an object with class `name`.
|
||||
|
@ -103,10 +122,10 @@ pub fn object_to_string(cx: *mut JSContext, name: &str) -> *mut JSString {
|
|||
}
|
||||
|
||||
/// Get the expando object, or null if there is none.
|
||||
pub fn get_expando_object(obj: *mut JSObject) -> *mut JSObject {
|
||||
pub fn get_expando_object(obj: HandleObject) -> *mut JSObject {
|
||||
unsafe {
|
||||
assert!(is_dom_proxy(obj));
|
||||
let val = GetProxyExtra(obj, JSPROXYSLOT_EXPANDO);
|
||||
assert!(is_dom_proxy(obj.get()));
|
||||
let val = GetProxyExtra(obj.get(), JSPROXYSLOT_EXPANDO);
|
||||
if val.is_undefined() {
|
||||
ptr::null_mut()
|
||||
} else {
|
||||
|
@ -117,18 +136,16 @@ pub fn get_expando_object(obj: *mut JSObject) -> *mut JSObject {
|
|||
|
||||
/// Get the expando object, or create it if it doesn't exist yet.
|
||||
/// Fails on JSAPI failure.
|
||||
pub fn ensure_expando_object(cx: *mut JSContext, obj: *mut JSObject)
|
||||
pub fn ensure_expando_object(cx: *mut JSContext, obj: HandleObject)
|
||||
-> *mut JSObject {
|
||||
unsafe {
|
||||
assert!(is_dom_proxy(obj));
|
||||
assert!(is_dom_proxy(obj.get()));
|
||||
let mut expando = get_expando_object(obj);
|
||||
if expando.is_null() {
|
||||
expando = JS_NewObjectWithGivenProto(cx, ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
GetObjectParent(obj));
|
||||
expando = JS_NewObjectWithGivenProto(cx, ptr::null_mut(), HandleObject::null());
|
||||
assert!(!expando.is_null());
|
||||
|
||||
SetProxyExtra(obj, JSPROXYSLOT_EXPANDO, ObjectValue(&*expando));
|
||||
SetProxyExtra(obj.get(), JSPROXYSLOT_EXPANDO, ObjectValue(&*expando));
|
||||
}
|
||||
return expando;
|
||||
}
|
||||
|
@ -142,18 +159,4 @@ pub fn fill_property_descriptor(desc: &mut JSPropertyDescriptor,
|
|||
desc.attrs = if readonly { JSPROP_READONLY } else { 0 } | JSPROP_ENUMERATE;
|
||||
desc.getter = None;
|
||||
desc.setter = None;
|
||||
desc.shortid = 0;
|
||||
}
|
||||
|
||||
/// No-op required hook.
|
||||
pub unsafe extern fn get_own_property_names(_cx: *mut JSContext,
|
||||
_obj: *mut JSObject,
|
||||
_v: *mut AutoIdVector) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
/// No-op required hook.
|
||||
pub unsafe extern fn enumerate(_cx: *mut JSContext, _obj: *mut JSObject,
|
||||
_v: *mut AutoIdVector) -> bool {
|
||||
true
|
||||
}
|
||||
|
|
|
@ -22,11 +22,12 @@
|
|||
//! is rooted when a hashmap entry is first created, and unrooted when the hashmap entry
|
||||
//! is removed.
|
||||
|
||||
use dom::bindings::js::{Temporary, JSRef, Unrooted};
|
||||
use dom::bindings::js::Root;
|
||||
use dom::bindings::utils::{Reflector, Reflectable};
|
||||
use dom::bindings::trace::trace_reflector;
|
||||
use script_task::{ScriptMsg, ScriptChan};
|
||||
|
||||
use js::jsapi::{JS_AddObjectRoot, JS_RemoveObjectRoot, JSContext};
|
||||
use js::jsapi::{JSContext, JSTracer};
|
||||
|
||||
use libc;
|
||||
use std::cell::RefCell;
|
||||
|
@ -35,6 +36,7 @@ use std::collections::hash_map::Entry::{Vacant, Occupied};
|
|||
use std::marker::PhantomData;
|
||||
use std::rc::Rc;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use core::nonzero::NonZero;
|
||||
|
||||
thread_local!(pub static LIVE_REFERENCES: Rc<RefCell<Option<LiveDOMReferences>>> = Rc::new(RefCell::new(None)));
|
||||
|
||||
|
@ -63,7 +65,7 @@ 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> {
|
||||
pub fn new(cx: *mut JSContext, ptr: &T, script_chan: Box<ScriptChan + Send>) -> Trusted<T> {
|
||||
LIVE_REFERENCES.with(|ref r| {
|
||||
let r = r.borrow();
|
||||
let live_references = r.as_ref().unwrap();
|
||||
|
@ -81,14 +83,14 @@ impl<T: Reflectable> Trusted<T> {
|
|||
/// 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> {
|
||||
pub fn root(&self) -> Root<T> {
|
||||
assert!(LIVE_REFERENCES.with(|ref r| {
|
||||
let r = r.borrow();
|
||||
let live_references = r.as_ref().unwrap();
|
||||
self.owner_thread == (&*live_references) as *const _ as *const libc::c_void
|
||||
}));
|
||||
unsafe {
|
||||
Temporary::from_unrooted(Unrooted::from_raw(self.ptr as *const T))
|
||||
Root::new(NonZero::new(self.ptr as *const T))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -151,10 +153,6 @@ impl LiveDOMReferences {
|
|||
refcount.clone()
|
||||
}
|
||||
Vacant(entry) => {
|
||||
unsafe {
|
||||
let rootable = (*ptr).reflector().rootable();
|
||||
JS_AddObjectRoot(cx, rootable);
|
||||
}
|
||||
let refcount = Arc::new(Mutex::new(1));
|
||||
entry.insert(refcount.clone());
|
||||
refcount
|
||||
|
@ -168,7 +166,6 @@ impl LiveDOMReferences {
|
|||
LIVE_REFERENCES.with(|ref r| {
|
||||
let r = r.borrow();
|
||||
let live_references = r.as_ref().unwrap();
|
||||
let reflectable = raw_reflectable as *const Reflector;
|
||||
let mut table = live_references.table.borrow_mut();
|
||||
match table.entry(raw_reflectable) {
|
||||
Occupied(entry) => {
|
||||
|
@ -178,9 +175,6 @@ impl LiveDOMReferences {
|
|||
return;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
JS_RemoveObjectRoot(cx, (*reflectable).rootable());
|
||||
}
|
||||
let _ = entry.remove();
|
||||
}
|
||||
Vacant(_) => {
|
||||
|
@ -194,3 +188,16 @@ impl LiveDOMReferences {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// A JSTraceDataOp for tracing reflectors held in LIVE_REFERENCES
|
||||
pub unsafe extern fn trace_refcounted_objects(tracer: *mut JSTracer, _data: *mut libc::c_void) {
|
||||
LIVE_REFERENCES.with(|ref r| {
|
||||
let r = r.borrow();
|
||||
let live_references = r.as_ref().unwrap();
|
||||
let table = live_references.table.borrow();
|
||||
for obj in table.keys() {
|
||||
let reflectable = &*(*obj as *const Reflector);
|
||||
trace_reflector(tracer, "LIVE_REFERENCES", reflectable);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ use js::glue::JS_STRUCTURED_CLONE_VERSION;
|
|||
use js::jsapi::JSContext;
|
||||
use js::jsapi::{JS_WriteStructuredClone, JS_ClearPendingException};
|
||||
use js::jsapi::JS_ReadStructuredClone;
|
||||
use js::jsval::{JSVal, UndefinedValue};
|
||||
use js::jsapi::{HandleValue, MutableHandleValue};
|
||||
|
||||
use libc::size_t;
|
||||
use std::ptr;
|
||||
|
@ -26,13 +26,14 @@ pub struct StructuredCloneData {
|
|||
|
||||
impl StructuredCloneData {
|
||||
/// Writes a structured clone. Returns a `DataClone` error if that fails.
|
||||
pub fn write(cx: *mut JSContext, message: JSVal)
|
||||
pub fn write(cx: *mut JSContext, message: HandleValue)
|
||||
-> Fallible<StructuredCloneData> {
|
||||
let mut data = ptr::null_mut();
|
||||
let mut nbytes = 0;
|
||||
let result = unsafe {
|
||||
JS_WriteStructuredClone(cx, message, &mut data, &mut nbytes,
|
||||
ptr::null(), ptr::null_mut())
|
||||
ptr::null(), ptr::null_mut(),
|
||||
HandleValue::undefined())
|
||||
};
|
||||
if result == 0 {
|
||||
unsafe { JS_ClearPendingException(cx); }
|
||||
|
@ -47,15 +48,13 @@ impl StructuredCloneData {
|
|||
/// Reads a structured clone.
|
||||
///
|
||||
/// Panics if `JS_ReadStructuredClone` fails.
|
||||
pub fn read(self, global: GlobalRef) -> JSVal {
|
||||
let mut message = UndefinedValue();
|
||||
pub fn read(self, global: GlobalRef, rval: MutableHandleValue) {
|
||||
unsafe {
|
||||
assert!(JS_ReadStructuredClone(
|
||||
global.get_cx(), self.data as *const u64, self.nbytes,
|
||||
JS_STRUCTURED_CLONE_VERSION, &mut message,
|
||||
global.get_cx(), self.data, self.nbytes,
|
||||
JS_STRUCTURED_CLONE_VERSION, rval,
|
||||
ptr::null(), ptr::null_mut()) != 0);
|
||||
}
|
||||
message
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -44,7 +44,8 @@ use euclid::size::Size2D;
|
|||
use html5ever::tree_builder::QuirksMode;
|
||||
use hyper::header::Headers;
|
||||
use hyper::method::Method;
|
||||
use js::jsapi::{JSObject, JSTracer, JS_CallTracer, JSGCTraceKind};
|
||||
use js::jsapi::{JSObject, JSTracer, JSGCTraceKind, JS_CallValueTracer, JS_CallObjectTracer, GCTraceKindToAscii, Heap};
|
||||
use js::jsapi::JS_CallUnbarrieredObjectTracer;
|
||||
use js::jsval::JSVal;
|
||||
use js::rust::Runtime;
|
||||
use layout_interface::{LayoutRPC, LayoutChan};
|
||||
|
@ -59,7 +60,7 @@ use msg::compositor_msg::ScriptListener;
|
|||
use msg::constellation_msg::ConstellationChan;
|
||||
use net_traits::image::base::Image;
|
||||
use util::str::{LengthOrPercentageOrAuto};
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::cell::{Cell, UnsafeCell, RefCell};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::collections::hash_state::HashState;
|
||||
use std::ffi::CString;
|
||||
|
@ -91,36 +92,46 @@ no_jsmanaged_fields!(EncodingRef);
|
|||
no_jsmanaged_fields!(Reflector);
|
||||
|
||||
/// Trace a `JSVal`.
|
||||
pub fn trace_jsval(tracer: *mut JSTracer, description: &str, val: JSVal) {
|
||||
if !val.is_markable() {
|
||||
return;
|
||||
}
|
||||
|
||||
pub fn trace_jsval(tracer: *mut JSTracer, description: &str, val: &Heap<JSVal>) {
|
||||
unsafe {
|
||||
if !val.get().is_markable() {
|
||||
return;
|
||||
}
|
||||
|
||||
let name = CString::new(description).unwrap();
|
||||
(*tracer).debugPrinter = None;
|
||||
(*tracer).debugPrintIndex = !0;
|
||||
(*tracer).debugPrintArg = name.as_ptr() as *const libc::c_void;
|
||||
(*tracer).debugPrinter_ = None;
|
||||
(*tracer).debugPrintIndex_ = !0;
|
||||
(*tracer).debugPrintArg_ = name.as_ptr() as *const libc::c_void;
|
||||
debug!("tracing value {}", description);
|
||||
JS_CallTracer(tracer, val.to_gcthing(), val.trace_kind());
|
||||
JS_CallValueTracer(tracer, val.ptr.get() as *mut _,
|
||||
GCTraceKindToAscii(val.get().trace_kind()));
|
||||
}
|
||||
}
|
||||
|
||||
/// Trace the `JSObject` held by `reflector`.
|
||||
#[allow(unrooted_must_root)]
|
||||
pub fn trace_reflector(tracer: *mut JSTracer, description: &str, reflector: &Reflector) {
|
||||
trace_object(tracer, description, reflector.get_jsobject())
|
||||
unsafe {
|
||||
let name = CString::new(description).unwrap();
|
||||
(*tracer).debugPrinter_ = None;
|
||||
(*tracer).debugPrintIndex_ = !0;
|
||||
(*tracer).debugPrintArg_ = name.as_ptr() as *const libc::c_void;
|
||||
debug!("tracing reflector {}", description);
|
||||
JS_CallUnbarrieredObjectTracer(tracer, reflector.rootable(),
|
||||
GCTraceKindToAscii(JSGCTraceKind::JSTRACE_OBJECT));
|
||||
}
|
||||
}
|
||||
|
||||
/// Trace a `JSObject`.
|
||||
pub fn trace_object(tracer: *mut JSTracer, description: &str, obj: *mut JSObject) {
|
||||
pub fn trace_object(tracer: *mut JSTracer, description: &str, obj: &Heap<*mut JSObject>) {
|
||||
unsafe {
|
||||
let name = CString::new(description).unwrap();
|
||||
(*tracer).debugPrinter = None;
|
||||
(*tracer).debugPrintIndex = !0;
|
||||
(*tracer).debugPrintArg = name.as_ptr() as *const libc::c_void;
|
||||
(*tracer).debugPrinter_ = None;
|
||||
(*tracer).debugPrintIndex_ = !0;
|
||||
(*tracer).debugPrintArg_ = name.as_ptr() as *const libc::c_void;
|
||||
debug!("tracing {}", description);
|
||||
JS_CallTracer(tracer, obj as *mut libc::c_void, JSGCTraceKind::JSTRACE_OBJECT);
|
||||
JS_CallObjectTracer(tracer, obj.ptr.get() as *mut _,
|
||||
GCTraceKindToAscii(JSGCTraceKind::JSTRACE_OBJECT));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -168,15 +179,26 @@ impl<T: JSTraceable+Copy> JSTraceable for Cell<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl JSTraceable for *mut JSObject {
|
||||
impl<T: JSTraceable> JSTraceable for UnsafeCell<T> {
|
||||
fn trace(&self, trc: *mut JSTracer) {
|
||||
trace_object(trc, "object", *self);
|
||||
unsafe { (*self.get()).trace(trc) }
|
||||
}
|
||||
}
|
||||
|
||||
impl JSTraceable for JSVal {
|
||||
|
||||
impl JSTraceable for Heap<*mut JSObject> {
|
||||
fn trace(&self, trc: *mut JSTracer) {
|
||||
trace_jsval(trc, "val", *self);
|
||||
if self.get().is_null() {
|
||||
return;
|
||||
}
|
||||
trace_object(trc, "object", self);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl JSTraceable for Heap<JSVal> {
|
||||
fn trace(&self, trc: *mut JSTracer) {
|
||||
trace_jsval(trc, "val", self);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -324,115 +346,101 @@ impl JSTraceable for () {
|
|||
}
|
||||
}
|
||||
|
||||
/// Holds a set of vectors that need to be rooted
|
||||
pub struct RootedCollectionSet {
|
||||
set: Vec<HashSet<*const RootedVec<Void>>>
|
||||
/// Homemade trait object for JSTraceable things
|
||||
struct TraceableInfo {
|
||||
pub ptr: *const libc::c_void,
|
||||
pub trace: fn(obj: *const libc::c_void, tracer: *mut JSTracer)
|
||||
}
|
||||
|
||||
/// TLV Holds a set of vectors that need to be rooted
|
||||
thread_local!(pub static ROOTED_COLLECTIONS: Rc<RefCell<RootedCollectionSet>> =
|
||||
Rc::new(RefCell::new(RootedCollectionSet::new())));
|
||||
|
||||
/// Type of `RootedVec`
|
||||
pub enum CollectionType {
|
||||
/// DOM objects
|
||||
DOMObjects,
|
||||
/// `JSVal`s
|
||||
JSVals,
|
||||
/// `*mut JSObject`s
|
||||
JSObjects,
|
||||
/// Holds a set of JSTraceables that need to be rooted
|
||||
pub struct RootedTraceableSet {
|
||||
set: Vec<TraceableInfo>
|
||||
}
|
||||
|
||||
/// TLV Holds a set of JSTraceables that need to be rooted
|
||||
thread_local!(pub static ROOTED_TRACEABLES: Rc<RefCell<RootedTraceableSet>> =
|
||||
Rc::new(RefCell::new(RootedTraceableSet::new())));
|
||||
|
||||
impl RootedCollectionSet {
|
||||
fn new() -> RootedCollectionSet {
|
||||
RootedCollectionSet {
|
||||
set: vec!(HashSet::new(), HashSet::new(), HashSet::new())
|
||||
impl RootedTraceableSet {
|
||||
fn new() -> RootedTraceableSet {
|
||||
RootedTraceableSet {
|
||||
set: vec!()
|
||||
}
|
||||
}
|
||||
|
||||
fn remove<T: VecRootableType>(collection: &RootedVec<T>) {
|
||||
ROOTED_COLLECTIONS.with(|ref collections| {
|
||||
let type_ = VecRootableType::tag(None::<T>);
|
||||
let mut collections = collections.borrow_mut();
|
||||
assert!(collections.set[type_ as usize].remove(&(collection as *const _ as *const _)));
|
||||
fn remove<T: JSTraceable>(traceable: &T) {
|
||||
ROOTED_TRACEABLES.with(|ref traceables| {
|
||||
let mut traceables = traceables.borrow_mut();
|
||||
let idx =
|
||||
match traceables.set.iter()
|
||||
.rposition(|x| x.ptr == traceable as *const T as *const _) {
|
||||
Some(idx) => idx,
|
||||
None => unreachable!(),
|
||||
};
|
||||
traceables.set.remove(idx);
|
||||
});
|
||||
}
|
||||
|
||||
fn add<T: VecRootableType>(collection: &RootedVec<T>) {
|
||||
ROOTED_COLLECTIONS.with(|ref collections| {
|
||||
let type_ = VecRootableType::tag(None::<T>);
|
||||
let mut collections = collections.borrow_mut();
|
||||
collections.set[type_ as usize].insert(collection as *const _ as *const _);
|
||||
fn add<T: JSTraceable>(traceable: &T) {
|
||||
ROOTED_TRACEABLES.with(|ref traceables| {
|
||||
fn trace<T: JSTraceable>(obj: *const libc::c_void, tracer: *mut JSTracer) {
|
||||
let obj: &T = unsafe { &*(obj as *const T) };
|
||||
obj.trace(tracer);
|
||||
}
|
||||
|
||||
let mut traceables = traceables.borrow_mut();
|
||||
let info = TraceableInfo {
|
||||
ptr: traceable as *const T as *const libc::c_void,
|
||||
trace: trace::<T>,
|
||||
};
|
||||
traceables.set.push(info);
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn trace(&self, tracer: *mut JSTracer) {
|
||||
fn trace_collection_type<T>(tracer: *mut JSTracer,
|
||||
collections: &HashSet<*const RootedVec<Void>>)
|
||||
where T: JSTraceable + VecRootableType
|
||||
{
|
||||
for collection in collections {
|
||||
let collection: *const RootedVec<Void> = *collection;
|
||||
let collection = collection as *const RootedVec<T>;
|
||||
unsafe {
|
||||
let _ = (*collection).trace(tracer);
|
||||
}
|
||||
}
|
||||
for info in self.set.iter() {
|
||||
(info.trace)(info.ptr, tracer);
|
||||
}
|
||||
|
||||
let dom_collections =
|
||||
&self.set[CollectionType::DOMObjects as usize] as *const _ as *const HashSet<*const RootedVec<JS<Void>>>;
|
||||
for dom_collection in (*dom_collections).iter() {
|
||||
for reflector in (**dom_collection).iter() {
|
||||
trace_reflector(tracer, "", reflector.reflector());
|
||||
}
|
||||
}
|
||||
|
||||
trace_collection_type::<JSVal>(tracer, &self.set[CollectionType::JSVals as usize]);
|
||||
trace_collection_type::<*mut JSObject>(tracer, &self.set[CollectionType::JSObjects as usize]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Trait implemented by all types that can be used with RootedVec
|
||||
pub trait VecRootableType {
|
||||
/// Return the type tag used to determine how to trace RootedVec
|
||||
fn tag(_a: Option<Self>) -> CollectionType;
|
||||
/// Roots any JSTraceable thing
|
||||
///
|
||||
/// If you have a valid Reflectable, use Root.
|
||||
/// If you have GC things like *mut JSObject or JSVal, use jsapi::Rooted.
|
||||
/// If you have an arbitrary number of Reflectables to root, use RootedVec<JS<T>>
|
||||
/// If you know what you're doing, use this.
|
||||
#[jstraceable]
|
||||
pub struct RootedTraceable<'a, T: 'a + JSTraceable> {
|
||||
ptr: &'a T
|
||||
}
|
||||
|
||||
impl<T: Reflectable> VecRootableType for JS<T> {
|
||||
fn tag(_a: Option<JS<T>>) -> CollectionType { CollectionType::DOMObjects }
|
||||
impl<'a, T: JSTraceable> RootedTraceable<'a, T> {
|
||||
/// Root a JSTraceable thing for the life of this RootedTraceable
|
||||
pub fn new(traceable: &'a T) -> RootedTraceable<'a, T> {
|
||||
RootedTraceableSet::add(traceable);
|
||||
RootedTraceable { ptr: traceable }
|
||||
}
|
||||
}
|
||||
|
||||
impl VecRootableType for JSVal {
|
||||
fn tag(_a: Option<JSVal>) -> CollectionType { CollectionType::JSVals }
|
||||
}
|
||||
|
||||
impl VecRootableType for *mut JSObject {
|
||||
fn tag(_a: Option<*mut JSObject>) -> CollectionType { CollectionType::JSObjects }
|
||||
}
|
||||
|
||||
enum Void {}
|
||||
|
||||
impl VecRootableType for Void {
|
||||
fn tag(_a: Option<Void>) -> CollectionType { unreachable!() }
|
||||
}
|
||||
|
||||
impl Reflectable for Void {
|
||||
fn reflector<'a>(&'a self) -> &'a Reflector { unreachable!() }
|
||||
impl<'a, T: JSTraceable> Drop for RootedTraceable<'a, T> {
|
||||
fn drop(&mut self) {
|
||||
RootedTraceableSet::remove(self.ptr);
|
||||
}
|
||||
}
|
||||
|
||||
/// A vector of items that are rooted for the lifetime
|
||||
/// of this struct
|
||||
/// of this struct.
|
||||
/// Must be a reflectable
|
||||
#[allow(unrooted_must_root)]
|
||||
#[no_move]
|
||||
pub struct RootedVec<T: VecRootableType> {
|
||||
#[jstraceable]
|
||||
pub struct RootedVec<T: JSTraceable + Reflectable> {
|
||||
v: Vec<T>
|
||||
}
|
||||
|
||||
|
||||
impl<T: VecRootableType> RootedVec<T> {
|
||||
impl<T: JSTraceable + Reflectable> RootedVec<T> {
|
||||
/// Create a vector of items of type T that is rooted for
|
||||
/// the lifetime of this struct
|
||||
pub fn new() -> RootedVec<T> {
|
||||
|
@ -444,39 +452,38 @@ impl<T: VecRootableType> RootedVec<T> {
|
|||
}
|
||||
|
||||
/// Create a vector of items of type T. This constructor is specific
|
||||
/// for RootCollection.
|
||||
/// for RootTraceableSet.
|
||||
pub fn new_with_destination_address(addr: *const libc::c_void) -> RootedVec<T> {
|
||||
unsafe {
|
||||
RootedCollectionSet::add::<T>(&*(addr as *const _));
|
||||
RootedTraceableSet::add::<RootedVec<T>>(&*(addr as *const _));
|
||||
}
|
||||
RootedVec::<T> { v: vec!() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: VecRootableType> Drop for RootedVec<T> {
|
||||
impl<T: JSTraceable + Reflectable> Drop for RootedVec<T> {
|
||||
fn drop(&mut self) {
|
||||
RootedCollectionSet::remove(self);
|
||||
RootedTraceableSet::remove(self);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: VecRootableType> Deref for RootedVec<T> {
|
||||
impl<T: JSTraceable + Reflectable> Deref for RootedVec<T> {
|
||||
type Target = Vec<T>;
|
||||
fn deref(&self) -> &Vec<T> {
|
||||
&self.v
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: VecRootableType> DerefMut for RootedVec<T> {
|
||||
impl<T: JSTraceable + Reflectable> DerefMut for RootedVec<T> {
|
||||
fn deref_mut(&mut self) -> &mut Vec<T> {
|
||||
&mut self.v
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// SM Callback that traces the rooted collections
|
||||
pub unsafe fn trace_collections(tracer: *mut JSTracer) {
|
||||
ROOTED_COLLECTIONS.with(|ref collections| {
|
||||
let collections = collections.borrow();
|
||||
collections.trace(tracer);
|
||||
/// SM Callback that traces the rooted traceables
|
||||
pub unsafe fn trace_traceables(tracer: *mut JSTracer) {
|
||||
ROOTED_TRACEABLES.with(|ref traceables| {
|
||||
let traceables = traceables.borrow();
|
||||
traceables.trace(tracer);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -6,10 +6,10 @@
|
|||
|
||||
use dom::bindings::codegen::PrototypeList;
|
||||
use dom::bindings::codegen::PrototypeList::MAX_PROTO_CHAIN_LENGTH;
|
||||
use dom::bindings::conversions::{native_from_reflector_jsmanaged, is_dom_class};
|
||||
use dom::bindings::conversions::{native_from_handleobject, is_dom_class, jsstring_to_str};
|
||||
use dom::bindings::error::{Error, ErrorResult, Fallible, throw_type_error};
|
||||
use dom::bindings::global::GlobalRef;
|
||||
use dom::bindings::js::{Temporary, Root, Rootable};
|
||||
use dom::bindings::js::Root;
|
||||
use dom::bindings::trace::trace_object;
|
||||
use dom::browsercontext;
|
||||
use dom::window;
|
||||
|
@ -19,30 +19,39 @@ use util::str::DOMString;
|
|||
use libc;
|
||||
use libc::c_uint;
|
||||
use std::boxed;
|
||||
use std::cell::Cell;
|
||||
use std::ffi::CString;
|
||||
use std::ptr;
|
||||
use std::cmp::PartialEq;
|
||||
use std::default::Default;
|
||||
use std::cell::UnsafeCell;
|
||||
use js::glue::UnwrapObject;
|
||||
use js::glue::{IsWrapper, RUST_JSID_IS_INT, RUST_JSID_TO_INT};
|
||||
use js::jsapi::{JS_AlreadyHasOwnProperty, JS_NewFunction};
|
||||
use js::jsapi::{JS_AlreadyHasOwnProperty, JS_NewFunction, JSTraceOp};
|
||||
use js::jsapi::{JS_DefineProperties, JS_ForwardGetPropertyTo};
|
||||
use js::jsapi::{JS_GetClass, JS_LinkConstructorAndPrototype, JS_GetStringCharsAndLength};
|
||||
use js::jsapi::{JSHandleObject, JSTracer};
|
||||
use js::jsapi::{JS_GetClass, JS_LinkConstructorAndPrototype};
|
||||
use js::jsapi::{HandleObject, HandleId, HandleValue, MutableHandleValue};
|
||||
use js::jsapi::JS_GetFunctionObject;
|
||||
use js::jsapi::{JS_HasPropertyById, JS_GetPrototype};
|
||||
use js::jsapi::{JS_GetProperty, JS_HasProperty, JS_SetProperty};
|
||||
use js::jsapi::{JS_DefineFunctions, JS_DefineProperty};
|
||||
use js::jsapi::{JS_ValueToString, JS_GetReservedSlot, JS_SetReservedSlot};
|
||||
use js::jsapi::{JSContext, JSObject, JSBool, jsid, JSClass};
|
||||
use js::jsapi::{JS_DefineFunctions, JS_DefineProperty, JS_DefineProperty1};
|
||||
use js::jsapi::{JS_GetReservedSlot, JS_SetReservedSlot};
|
||||
use js::jsapi::{JSContext, JSObject, JSClass, JSTracer};
|
||||
use js::jsapi::{JSFunctionSpec, JSPropertySpec};
|
||||
use js::jsapi::{JS_NewGlobalObject, JS_InitStandardClasses};
|
||||
use js::jsapi::JS_DeletePropertyById2;
|
||||
use js::jsfriendapi::JS_ObjectToOuterObject;
|
||||
use js::jsfriendapi::bindgen::JS_NewObjectWithUniqueType;
|
||||
use js::jsapi::{OnNewGlobalHookOption, CompartmentOptions};
|
||||
use js::jsapi::{JS_FireOnNewGlobalObject, JSVersion};
|
||||
use js::jsapi::JS_DeletePropertyById1;
|
||||
use js::jsapi::JS_ObjectToOuterObject;
|
||||
use js::jsapi::JS_NewObjectWithUniqueType;
|
||||
use js::jsapi::{ObjectOpResult, RootedObject, RootedValue, Heap, MutableHandleObject};
|
||||
use js::jsapi::PropertyDefinitionBehavior;
|
||||
use js::jsapi::JSAutoCompartment;
|
||||
use js::jsapi::{DOMCallbacks, JSWrapObjectCallbacks};
|
||||
use js::jsval::JSVal;
|
||||
use js::jsval::{PrivateValue, ObjectValue, NullValue};
|
||||
use js::jsval::{Int32Value, UInt32Value, DoubleValue, BooleanValue, UndefinedValue};
|
||||
use js::rust::with_compartment;
|
||||
use js::jsval::{PrivateValue, NullValue};
|
||||
use js::jsval::{Int32Value, UInt32Value, DoubleValue, BooleanValue};
|
||||
use js::rust::{GCMethods, ToString};
|
||||
use js::glue::{WrapperNew, GetCrossCompartmentWrapper};
|
||||
use js::{JSPROP_ENUMERATE, JSPROP_READONLY, JSPROP_PERMANENT};
|
||||
use js::JSFUN_CONSTRUCTOR;
|
||||
use js;
|
||||
|
@ -145,7 +154,7 @@ unsafe impl Sync for DOMClass {}
|
|||
#[derive(Copy)]
|
||||
pub struct DOMJSClass {
|
||||
/// The actual JSClass.
|
||||
pub base: js::Class,
|
||||
pub base: js::jsapi::Class,
|
||||
/// Associated data for DOM object reflectors.
|
||||
pub dom_class: DOMClass
|
||||
}
|
||||
|
@ -181,95 +190,93 @@ unsafe impl Sync for NativeProperties {}
|
|||
|
||||
/// A JSNative that cannot be null.
|
||||
pub type NonNullJSNative =
|
||||
unsafe extern "C" fn (arg1: *mut JSContext, arg2: c_uint, arg3: *mut JSVal) -> JSBool;
|
||||
unsafe extern "C" fn (arg1: *mut JSContext, arg2: c_uint, arg3: *mut JSVal) -> u8;
|
||||
|
||||
/// Creates the *interface prototype object* (if a `proto_class` is given)
|
||||
/// and the *interface object* (if a `constructor` is given).
|
||||
/// Fails on JSAPI failure.
|
||||
pub fn do_create_interface_objects(cx: *mut JSContext, global: *mut JSObject,
|
||||
receiver: *mut JSObject,
|
||||
proto_proto: *mut JSObject,
|
||||
pub fn do_create_interface_objects(cx: *mut JSContext,
|
||||
receiver: HandleObject,
|
||||
proto_proto: HandleObject,
|
||||
proto_class: Option<&'static JSClass>,
|
||||
constructor: Option<(NonNullJSNative, &'static str, u32)>,
|
||||
dom_class: *const DOMClass,
|
||||
members: &'static NativeProperties)
|
||||
-> *mut JSObject {
|
||||
members: &'static NativeProperties,
|
||||
rval: MutableHandleObject) {
|
||||
if let Some(proto_class) = proto_class {
|
||||
create_interface_prototype_object(cx, proto_proto,
|
||||
proto_class, members, rval);
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let proto = match proto_class {
|
||||
Some(proto_class) => {
|
||||
let proto = create_interface_prototype_object(cx, global, proto_proto,
|
||||
proto_class, members);
|
||||
JS_SetReservedSlot(proto, DOM_PROTO_INSTANCE_CLASS_SLOT,
|
||||
PrivateValue(dom_class as *const libc::c_void));
|
||||
proto
|
||||
},
|
||||
None => ptr::null_mut()
|
||||
};
|
||||
|
||||
if let Some((native, name, nargs)) = constructor {
|
||||
let s = CString::new(name).unwrap();
|
||||
create_interface_object(cx, global, receiver,
|
||||
native, nargs, proto,
|
||||
members, s.as_ptr())
|
||||
if !rval.get().is_null() {
|
||||
JS_SetReservedSlot(rval.get(), DOM_PROTO_INSTANCE_CLASS_SLOT,
|
||||
PrivateValue(dom_class as *const libc::c_void));
|
||||
}
|
||||
}
|
||||
|
||||
proto
|
||||
if let Some((native, name, nargs)) = constructor {
|
||||
let s = CString::new(name).unwrap();
|
||||
create_interface_object(cx, receiver,
|
||||
native, nargs, rval.handle(),
|
||||
members, s.as_ptr())
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates the *interface object*.
|
||||
/// Fails on JSAPI failure.
|
||||
fn create_interface_object(cx: *mut JSContext, global: *mut JSObject,
|
||||
receiver: *mut JSObject,
|
||||
fn create_interface_object(cx: *mut JSContext,
|
||||
receiver: HandleObject,
|
||||
constructor_native: NonNullJSNative,
|
||||
ctor_nargs: u32, proto: *mut JSObject,
|
||||
ctor_nargs: u32, proto: HandleObject,
|
||||
members: &'static NativeProperties,
|
||||
name: *const libc::c_char) {
|
||||
unsafe {
|
||||
let fun = JS_NewFunction(cx, Some(constructor_native), ctor_nargs,
|
||||
JSFUN_CONSTRUCTOR, global, name);
|
||||
JSFUN_CONSTRUCTOR, name);
|
||||
assert!(!fun.is_null());
|
||||
|
||||
let constructor = JS_GetFunctionObject(fun);
|
||||
assert!(!constructor.is_null());
|
||||
let constructor = RootedObject::new(cx, JS_GetFunctionObject(fun));
|
||||
assert!(!constructor.ptr.is_null());
|
||||
|
||||
if let Some(static_methods) = members.static_methods {
|
||||
define_methods(cx, constructor, static_methods);
|
||||
define_methods(cx, constructor.handle(), static_methods);
|
||||
}
|
||||
|
||||
if let Some(static_properties) = members.static_attrs {
|
||||
define_properties(cx, constructor, static_properties);
|
||||
define_properties(cx, constructor.handle(), static_properties);
|
||||
}
|
||||
|
||||
if let Some(constants) = members.consts {
|
||||
define_constants(cx, constructor, constants);
|
||||
define_constants(cx, constructor.handle(), constants);
|
||||
}
|
||||
|
||||
if !proto.is_null() {
|
||||
assert!(JS_LinkConstructorAndPrototype(cx, constructor, proto) != 0);
|
||||
if !proto.get().is_null() {
|
||||
assert!(JS_LinkConstructorAndPrototype(cx, constructor.handle(), proto) != 0);
|
||||
}
|
||||
|
||||
let mut already_defined = 0;
|
||||
assert!(JS_AlreadyHasOwnProperty(cx, receiver, name, &mut already_defined) != 0);
|
||||
|
||||
if already_defined == 0 {
|
||||
assert!(JS_DefineProperty(cx, receiver, name,
|
||||
ObjectValue(&*constructor),
|
||||
None, None, 0) != 0);
|
||||
assert!(JS_DefineProperty1(cx, receiver, name,
|
||||
constructor.handle(),
|
||||
0, None, None) != 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Defines constants on `obj`.
|
||||
/// Fails on JSAPI failure.
|
||||
fn define_constants(cx: *mut JSContext, obj: *mut JSObject,
|
||||
fn define_constants(cx: *mut JSContext, obj: HandleObject,
|
||||
constants: &'static [ConstantSpec]) {
|
||||
for spec in constants.iter() {
|
||||
let value = RootedValue::new(cx, spec.get_value());
|
||||
unsafe {
|
||||
assert!(JS_DefineProperty(cx, obj, spec.name.as_ptr() as *const libc::c_char,
|
||||
spec.get_value(), None, None,
|
||||
value.handle(),
|
||||
JSPROP_ENUMERATE | JSPROP_READONLY |
|
||||
JSPROP_PERMANENT) != 0);
|
||||
JSPROP_PERMANENT, None, None) != 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -277,17 +284,17 @@ fn define_constants(cx: *mut JSContext, obj: *mut JSObject,
|
|||
/// Defines methods on `obj`. The last entry of `methods` must contain zeroed
|
||||
/// memory.
|
||||
/// Fails on JSAPI failure.
|
||||
fn define_methods(cx: *mut JSContext, obj: *mut JSObject,
|
||||
fn define_methods(cx: *mut JSContext, obj: HandleObject,
|
||||
methods: &'static [JSFunctionSpec]) {
|
||||
unsafe {
|
||||
assert!(JS_DefineFunctions(cx, obj, methods.as_ptr()) != 0);
|
||||
assert!(JS_DefineFunctions(cx, obj, methods.as_ptr(), PropertyDefinitionBehavior::DefineAllProperties) != 0);
|
||||
}
|
||||
}
|
||||
|
||||
/// Defines attributes on `obj`. The last entry of `properties` must contain
|
||||
/// zeroed memory.
|
||||
/// Fails on JSAPI failure.
|
||||
fn define_properties(cx: *mut JSContext, obj: *mut JSObject,
|
||||
fn define_properties(cx: *mut JSContext, obj: HandleObject,
|
||||
properties: &'static [JSPropertySpec]) {
|
||||
unsafe {
|
||||
assert!(JS_DefineProperties(cx, obj, properties.as_ptr()) != 0);
|
||||
|
@ -296,36 +303,32 @@ fn define_properties(cx: *mut JSContext, obj: *mut JSObject,
|
|||
|
||||
/// Creates the *interface prototype object*.
|
||||
/// Fails on JSAPI failure.
|
||||
fn create_interface_prototype_object(cx: *mut JSContext, global: *mut JSObject,
|
||||
parent_proto: *mut JSObject,
|
||||
fn create_interface_prototype_object(cx: *mut JSContext, global: HandleObject,
|
||||
proto_class: &'static JSClass,
|
||||
members: &'static NativeProperties)
|
||||
-> *mut JSObject {
|
||||
members: &'static NativeProperties,
|
||||
rval: MutableHandleObject) {
|
||||
unsafe {
|
||||
let our_proto = JS_NewObjectWithUniqueType(cx, proto_class,
|
||||
&*parent_proto, &*global);
|
||||
assert!(!our_proto.is_null());
|
||||
rval.set(JS_NewObjectWithUniqueType(cx, proto_class, global));
|
||||
assert!(!rval.get().is_null());
|
||||
|
||||
if let Some(methods) = members.methods {
|
||||
define_methods(cx, our_proto, methods);
|
||||
define_methods(cx, rval.handle(), methods);
|
||||
}
|
||||
|
||||
if let Some(properties) = members.attrs {
|
||||
define_properties(cx, our_proto, properties);
|
||||
define_properties(cx, rval.handle(), properties);
|
||||
}
|
||||
|
||||
if let Some(constants) = members.consts {
|
||||
define_constants(cx, our_proto, constants);
|
||||
define_constants(cx, rval.handle(), constants);
|
||||
}
|
||||
|
||||
return our_proto;
|
||||
}
|
||||
}
|
||||
|
||||
/// A throwing constructor, for those interfaces that have neither
|
||||
/// `NoInterfaceObject` nor `Constructor`.
|
||||
pub unsafe extern fn throwing_constructor(cx: *mut JSContext, _argc: c_uint,
|
||||
_vp: *mut JSVal) -> JSBool {
|
||||
_vp: *mut JSVal) -> u8 {
|
||||
throw_type_error(cx, "Illegal constructor.");
|
||||
return 0;
|
||||
}
|
||||
|
@ -351,6 +354,10 @@ pub fn initialize_global(global: *mut JSObject) {
|
|||
pub trait Reflectable {
|
||||
/// Returns the receiver's reflector.
|
||||
fn reflector<'a>(&'a self) -> &'a Reflector;
|
||||
/// Initializes the Reflector
|
||||
fn init_reflector(&mut self, _obj: *mut JSObject) {
|
||||
panic!("Cannot call init on this Reflectable");
|
||||
}
|
||||
}
|
||||
|
||||
/// Create the reflector for a new DOM object and yield ownership to the
|
||||
|
@ -358,47 +365,56 @@ pub trait Reflectable {
|
|||
pub fn reflect_dom_object<T: Reflectable>
|
||||
(obj: Box<T>,
|
||||
global: GlobalRef,
|
||||
wrap_fn: extern "Rust" fn(*mut JSContext, GlobalRef, Box<T>) -> Temporary<T>)
|
||||
-> Temporary<T> {
|
||||
wrap_fn: extern "Rust" fn(*mut JSContext, GlobalRef, Box<T>) -> Root<T>)
|
||||
-> Root<T> {
|
||||
wrap_fn(global.get_cx(), global, obj)
|
||||
}
|
||||
|
||||
/// A struct to store a reference to the reflector of a DOM object.
|
||||
// Allowing unused_attribute because the lint sometimes doesn't run in order
|
||||
#[allow(raw_pointer_derive, unrooted_must_root, unused_attributes)]
|
||||
#[derive(PartialEq)]
|
||||
#[must_root]
|
||||
#[servo_lang = "reflector"]
|
||||
// If you're renaming or moving this field, update the path in plugins::reflector as well
|
||||
pub struct Reflector {
|
||||
object: Cell<*mut JSObject>,
|
||||
object: UnsafeCell<*mut JSObject>,
|
||||
}
|
||||
|
||||
#[allow(unrooted_must_root)]
|
||||
impl PartialEq for Reflector {
|
||||
fn eq(&self, other: &Reflector) -> bool {
|
||||
unsafe { *self.object.get() == *other.object.get() }
|
||||
}
|
||||
}
|
||||
|
||||
impl Reflector {
|
||||
/// Get the reflector.
|
||||
#[inline]
|
||||
pub fn get_jsobject(&self) -> *mut JSObject {
|
||||
self.object.get()
|
||||
pub fn get_jsobject(&self) -> HandleObject {
|
||||
HandleObject { ptr: self.object.get() }
|
||||
}
|
||||
|
||||
/// Initialize the reflector. (May be called only once.)
|
||||
pub fn set_jsobject(&self, object: *mut JSObject) {
|
||||
assert!(self.object.get().is_null());
|
||||
assert!(!object.is_null());
|
||||
self.object.set(object);
|
||||
pub fn set_jsobject(&mut self, object: *mut JSObject) {
|
||||
unsafe {
|
||||
let obj = self.object.get();
|
||||
assert!((*obj).is_null());
|
||||
assert!(!object.is_null());
|
||||
*obj = object;
|
||||
}
|
||||
}
|
||||
|
||||
/// Return a pointer to the memory location at which the JS reflector
|
||||
/// object is stored. Used by Temporary values to root the reflector, as
|
||||
/// object is stored. Used to root the reflector, as
|
||||
/// required by the JSAPI rooting APIs.
|
||||
pub unsafe fn rootable(&self) -> *mut *mut JSObject {
|
||||
self.object.as_unsafe_cell().get()
|
||||
pub fn rootable(&self) -> *mut *mut JSObject {
|
||||
self.object.get()
|
||||
}
|
||||
|
||||
/// Create an uninitialized `Reflector`.
|
||||
pub fn new() -> Reflector {
|
||||
Reflector {
|
||||
object: Cell::new(ptr::null_mut()),
|
||||
object: UnsafeCell::new(ptr::null_mut())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -407,33 +423,34 @@ impl Reflector {
|
|||
/// set to true and `*vp` to the value, otherwise `*found` is set to false.
|
||||
///
|
||||
/// Returns false on JSAPI failure.
|
||||
pub fn get_property_on_prototype(cx: *mut JSContext, proxy: *mut JSObject,
|
||||
id: jsid, found: *mut bool, vp: *mut JSVal)
|
||||
pub fn get_property_on_prototype(cx: *mut JSContext, proxy: HandleObject,
|
||||
id: HandleId, found: *mut bool, vp: MutableHandleValue)
|
||||
-> bool {
|
||||
unsafe {
|
||||
//let proto = GetObjectProto(proxy);
|
||||
let proto = JS_GetPrototype(proxy);
|
||||
if proto.is_null() {
|
||||
let mut proto = RootedObject::new(cx, ptr::null_mut());
|
||||
if JS_GetPrototype(cx, proxy, proto.handle_mut()) == 0 ||
|
||||
proto.ptr.is_null() {
|
||||
*found = false;
|
||||
return true;
|
||||
}
|
||||
let mut has_property = 0;
|
||||
if JS_HasPropertyById(cx, proto, id, &mut has_property) == 0 {
|
||||
if JS_HasPropertyById(cx, proto.handle(), id, &mut has_property) == 0 {
|
||||
return false;
|
||||
}
|
||||
*found = has_property != 0;
|
||||
let no_output = vp.is_null();
|
||||
let no_output = vp.ptr.is_null();
|
||||
if has_property == 0 || no_output {
|
||||
return true;
|
||||
}
|
||||
|
||||
JS_ForwardGetPropertyTo(cx, proto, id, proxy, vp) != 0
|
||||
JS_ForwardGetPropertyTo(cx, proto.handle(), id, proxy, vp) != 0
|
||||
}
|
||||
}
|
||||
|
||||
/// Get an array index from the given `jsid`. Returns `None` if the given
|
||||
/// `jsid` is not an integer.
|
||||
pub fn get_array_index_from_id(_cx: *mut JSContext, id: jsid) -> Option<u32> {
|
||||
pub fn get_array_index_from_id(_cx: *mut JSContext, id: HandleId) -> Option<u32> {
|
||||
unsafe {
|
||||
if RUST_JSID_IS_INT(id) != 0 {
|
||||
return Some(RUST_JSID_TO_INT(id) as u32);
|
||||
|
@ -460,28 +477,16 @@ pub fn get_array_index_from_id(_cx: *mut JSContext, id: jsid) -> Option<u32> {
|
|||
/// Returns `Err(())` on JSAPI failure (there is a pending exception), and
|
||||
/// `Ok(None)` if there was no matching string.
|
||||
pub fn find_enum_string_index(cx: *mut JSContext,
|
||||
v: JSVal,
|
||||
v: HandleValue,
|
||||
values: &[&'static str])
|
||||
-> Result<Option<usize>, ()> {
|
||||
unsafe {
|
||||
let jsstr = JS_ValueToString(cx, v);
|
||||
if jsstr.is_null() {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
let mut length = 0;
|
||||
let chars = JS_GetStringCharsAndLength(cx, jsstr, &mut length);
|
||||
if chars.is_null() {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
Ok(values.iter().position(|value| {
|
||||
value.len() == length as usize &&
|
||||
(0..length as usize).all(|j| {
|
||||
value.as_bytes()[j] as u16 == *chars.offset(j as isize)
|
||||
})
|
||||
}))
|
||||
let jsstr = ToString(cx, v);
|
||||
if jsstr.is_null() {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
let search = jsstring_to_str(cx, jsstr);
|
||||
Ok(values.iter().position(|value| value == &search))
|
||||
}
|
||||
|
||||
/// Returns wether `obj` is a platform object
|
||||
|
@ -495,7 +500,7 @@ pub fn is_platform_object(obj: *mut JSObject) -> bool {
|
|||
}
|
||||
// Now for simplicity check for security wrappers before anything else
|
||||
if IsWrapper(obj) == 1 {
|
||||
let unwrapped_obj = UnwrapObject(obj, /* stopAtOuter = */ 0, ptr::null_mut());
|
||||
let unwrapped_obj = UnwrapObject(obj, /* stopAtOuter = */ 0);
|
||||
if unwrapped_obj.is_null() {
|
||||
return false;
|
||||
}
|
||||
|
@ -508,53 +513,54 @@ pub fn is_platform_object(obj: *mut JSObject) -> bool {
|
|||
|
||||
/// Get the property with name `property` from `object`.
|
||||
/// Returns `Err(())` on JSAPI failure (there is a pending exception), and
|
||||
/// `Ok(None)` if there was no property with the given name.
|
||||
/// `Ok(false)` if there was no property with the given name.
|
||||
pub fn get_dictionary_property(cx: *mut JSContext,
|
||||
object: *mut JSObject,
|
||||
property: &str) -> Result<Option<JSVal>, ()> {
|
||||
fn has_property(cx: *mut JSContext, object: *mut JSObject, property: &CString,
|
||||
found: &mut JSBool) -> bool {
|
||||
object: HandleObject,
|
||||
property: &str,
|
||||
rval: MutableHandleValue)
|
||||
-> Result<bool, ()> {
|
||||
fn has_property(cx: *mut JSContext, object: HandleObject, property: &CString,
|
||||
found: &mut u8) -> bool {
|
||||
unsafe {
|
||||
JS_HasProperty(cx, object, property.as_ptr(), found) != 0
|
||||
}
|
||||
}
|
||||
fn get_property(cx: *mut JSContext, object: *mut JSObject, property: &CString,
|
||||
value: &mut JSVal) -> bool {
|
||||
fn get_property(cx: *mut JSContext, object: HandleObject, property: &CString,
|
||||
value: MutableHandleValue) -> bool {
|
||||
unsafe {
|
||||
JS_GetProperty(cx, object, property.as_ptr(), value) != 0
|
||||
}
|
||||
}
|
||||
|
||||
let property = CString::new(property).unwrap();
|
||||
if object.is_null() {
|
||||
return Ok(None);
|
||||
if object.get().is_null() {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
let mut found: JSBool = 0;
|
||||
let mut found: u8 = 0;
|
||||
if !has_property(cx, object, &property, &mut found) {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
if found == 0 {
|
||||
return Ok(None);
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
let mut value = NullValue();
|
||||
if !get_property(cx, object, &property, &mut value) {
|
||||
if !get_property(cx, object, &property, rval) {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
Ok(Some(value))
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
/// Set the property with name `property` from `object`.
|
||||
/// Returns `Err(())` on JSAPI failure, or null object,
|
||||
/// and Ok(()) otherwise
|
||||
pub fn set_dictionary_property(cx: *mut JSContext,
|
||||
object: *mut JSObject,
|
||||
object: HandleObject,
|
||||
property: &str,
|
||||
value: &mut JSVal) -> Result<(), ()> {
|
||||
if object.is_null() {
|
||||
value: HandleValue) -> Result<(), ()> {
|
||||
if object.get().is_null() {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
|
@ -569,79 +575,99 @@ pub fn set_dictionary_property(cx: *mut JSContext,
|
|||
}
|
||||
|
||||
/// Returns whether `proxy` has a property `id` on its prototype.
|
||||
pub fn has_property_on_prototype(cx: *mut JSContext, proxy: *mut JSObject,
|
||||
id: jsid) -> bool {
|
||||
pub fn has_property_on_prototype(cx: *mut JSContext, proxy: HandleObject,
|
||||
id: HandleId) -> bool {
|
||||
// MOZ_ASSERT(js::IsProxy(proxy) && js::GetProxyHandler(proxy) == handler);
|
||||
let mut found = false;
|
||||
return !get_property_on_prototype(cx, proxy, id, &mut found, ptr::null_mut()) || found;
|
||||
return !get_property_on_prototype(cx, proxy, id, &mut found,
|
||||
MutableHandleValue { ptr: ptr::null_mut() }) || found;
|
||||
}
|
||||
|
||||
/// Create a DOM global object with the given class.
|
||||
pub fn create_dom_global(cx: *mut JSContext, class: *const JSClass)
|
||||
pub fn create_dom_global(cx: *mut JSContext, class: *const JSClass,
|
||||
trace: JSTraceOp)
|
||||
-> *mut JSObject {
|
||||
unsafe {
|
||||
let obj = JS_NewGlobalObject(cx, class, ptr::null_mut());
|
||||
if obj.is_null() {
|
||||
let mut options = CompartmentOptions::default();
|
||||
options.version_ = JSVersion::JSVERSION_LATEST;
|
||||
options.traceGlobal_ = trace;
|
||||
|
||||
let obj =
|
||||
RootedObject::new(cx,
|
||||
JS_NewGlobalObject(cx, class, ptr::null_mut(),
|
||||
OnNewGlobalHookOption::DontFireOnNewGlobalHook, &options));
|
||||
if obj.ptr.is_null() {
|
||||
return ptr::null_mut();
|
||||
}
|
||||
with_compartment(cx, obj, || {
|
||||
JS_InitStandardClasses(cx, obj);
|
||||
});
|
||||
initialize_global(obj);
|
||||
obj
|
||||
let _ac = JSAutoCompartment::new(cx, obj.ptr);
|
||||
JS_InitStandardClasses(cx, obj.handle());
|
||||
initialize_global(obj.ptr);
|
||||
JS_FireOnNewGlobalObject(cx, obj.handle());
|
||||
obj.ptr
|
||||
}
|
||||
}
|
||||
|
||||
/// Drop the resources held by reserved slots of a global object
|
||||
pub unsafe fn finalize_global(obj: *mut JSObject) {
|
||||
let protolist = get_proto_or_iface_array(obj);
|
||||
let list = (*protolist).as_mut_ptr();
|
||||
for idx in 0..(PrototypeList::ID::Count as isize) {
|
||||
let entry = list.offset(idx);
|
||||
let value = *entry;
|
||||
if <*mut JSObject>::needs_post_barrier(value) {
|
||||
<*mut JSObject>::relocate(entry);
|
||||
}
|
||||
}
|
||||
let _: Box<ProtoOrIfaceArray> =
|
||||
Box::from_raw(get_proto_or_iface_array(obj));
|
||||
Box::from_raw(protolist);
|
||||
}
|
||||
|
||||
/// Trace the resources held by reserved slots of a global object
|
||||
pub unsafe fn trace_global(tracer: *mut JSTracer, obj: *mut JSObject) {
|
||||
let array = get_proto_or_iface_array(obj);
|
||||
for &proto in (*array).iter() {
|
||||
for proto in (&*array).iter() {
|
||||
if !proto.is_null() {
|
||||
trace_object(tracer, "prototype", proto);
|
||||
trace_object(tracer, "prototype", &*(proto as *const *mut JSObject as *const Heap<*mut JSObject>));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Callback to outerize windows when wrapping.
|
||||
pub unsafe extern fn wrap_for_same_compartment(cx: *mut JSContext, obj: *mut JSObject) -> *mut JSObject {
|
||||
unsafe extern fn wrap(cx: *mut JSContext,
|
||||
existing: HandleObject,
|
||||
obj: HandleObject)
|
||||
-> *mut JSObject {
|
||||
// FIXME terrible idea. need security wrappers
|
||||
// https://github.com/servo/servo/issues/2382
|
||||
WrapperNew(cx, obj, GetCrossCompartmentWrapper())
|
||||
}
|
||||
|
||||
unsafe extern fn pre_wrap(cx: *mut JSContext, _existing: HandleObject,
|
||||
obj: HandleObject, _object_passed_to_wrap: HandleObject)
|
||||
-> *mut JSObject {
|
||||
let _ac = JSAutoCompartment::new(cx, obj.get());
|
||||
JS_ObjectToOuterObject(cx, obj)
|
||||
}
|
||||
|
||||
/// Callback to outerize windows before wrapping.
|
||||
pub unsafe extern fn pre_wrap(cx: *mut JSContext, _scope: *mut JSObject,
|
||||
obj: *mut JSObject, _flags: c_uint) -> *mut JSObject {
|
||||
JS_ObjectToOuterObject(cx, obj)
|
||||
}
|
||||
/// Callback table for use with JS_SetWrapObjectCallbacks
|
||||
pub static WRAP_CALLBACKS: JSWrapObjectCallbacks = JSWrapObjectCallbacks {
|
||||
wrap: Some(wrap),
|
||||
preWrap: Some(pre_wrap),
|
||||
};
|
||||
|
||||
/// Callback to outerize windows.
|
||||
pub extern fn outerize_global(_cx: *mut JSContext, obj: JSHandleObject) -> *mut JSObject {
|
||||
unsafe {
|
||||
debug!("outerizing");
|
||||
let obj = *obj.unnamed_field1;
|
||||
let win: Root<window::Window> = native_from_reflector_jsmanaged(obj).unwrap().root();
|
||||
// FIXME(https://github.com/rust-lang/rust/issues/23338)
|
||||
let win = win.r();
|
||||
let context = win.browser_context();
|
||||
context.as_ref().unwrap().window_proxy()
|
||||
}
|
||||
pub unsafe extern fn outerize_global(_cx: *mut JSContext, obj: HandleObject) -> *mut JSObject {
|
||||
debug!("outerizing");
|
||||
let win: Root<window::Window> = native_from_handleobject(obj).unwrap();
|
||||
// FIXME(https://github.com/rust-lang/rust/issues/23338)
|
||||
let win = win.r();
|
||||
let context = win.browser_context();
|
||||
context.as_ref().unwrap().window_proxy()
|
||||
}
|
||||
|
||||
/// Deletes the property `id` from `object`.
|
||||
pub unsafe fn delete_property_by_id(cx: *mut JSContext, object: *mut JSObject,
|
||||
id: jsid, bp: &mut bool) -> bool {
|
||||
let mut value = UndefinedValue();
|
||||
if JS_DeletePropertyById2(cx, object, id, &mut value) == 0 {
|
||||
return false;
|
||||
}
|
||||
|
||||
*bp = value.to_boolean();
|
||||
return true;
|
||||
pub unsafe fn delete_property_by_id(cx: *mut JSContext, object: HandleObject,
|
||||
id: HandleId, bp: *mut ObjectOpResult) -> u8 {
|
||||
JS_DeletePropertyById1(cx, object, id, bp)
|
||||
}
|
||||
|
||||
/// Validate a qualified name. See https://dom.spec.whatwg.org/#validate for details.
|
||||
|
@ -659,6 +685,18 @@ pub fn validate_qualified_name(qualified_name: &str) -> ErrorResult {
|
|||
}
|
||||
}
|
||||
|
||||
unsafe extern "C" fn instance_class_has_proto_at_depth(clasp: *const js::jsapi::Class,
|
||||
proto_id: u32,
|
||||
depth: u32) -> u8 {
|
||||
let domclass: *const DOMJSClass = clasp as *const _;
|
||||
let domclass = &*domclass;
|
||||
(domclass.dom_class.interface_chain[depth as usize] as u32 == proto_id) as u8
|
||||
}
|
||||
|
||||
pub const DOM_CALLBACKS: DOMCallbacks = DOMCallbacks {
|
||||
instanceClassMatchesProto: Some(instance_class_has_proto_at_depth),
|
||||
};
|
||||
|
||||
/// Validate a namespace and qualified name and extract their parts.
|
||||
/// See https://dom.spec.whatwg.org/#validate-and-extract for details.
|
||||
pub fn validate_and_extract(namespace: Option<DOMString>, qualified_name: &str)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue