diff options
Diffstat (limited to 'components/script/dom/bindings/conversions.rs')
-rw-r--r-- | components/script/dom/bindings/conversions.rs | 503 |
1 files changed, 342 insertions, 161 deletions
diff --git a/components/script/dom/bindings/conversions.rs b/components/script/dom/bindings/conversions.rs index d77f6ea931c..4964180753d 100644 --- a/components/script/dom/bindings/conversions.rs +++ b/components/script/dom/bindings/conversions.rs @@ -1,71 +1,80 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ //! Conversions of Rust values to and from `JSVal`. //! //! | IDL type | Argument type | Return type | //! |-------------------------|-----------------|----------------| -//! | any | `JSVal` | -//! | boolean | `bool` | -//! | byte | `i8` | -//! | octet | `u8` | -//! | short | `i16` | -//! | unsigned short | `u16` | -//! | long | `i32` | -//! | unsigned long | `u32` | -//! | long long | `i64` | -//! | unsigned long long | `u64` | -//! | unrestricted float | `f32` | -//! | float | `Finite<f32>` | -//! | unrestricted double | `f64` | -//! | double | `Finite<f64>` | -//! | DOMString | `DOMString` | -//! | USVString | `USVString` | -//! | ByteString | `ByteString` | -//! | object | `*mut JSObject` | -//! | interface types | `&T` | `Root<T>` | +//! | any | `JSVal` | | +//! | boolean | `bool` | | +//! | byte | `i8` | | +//! | octet | `u8` | | +//! | short | `i16` | | +//! | unsigned short | `u16` | | +//! | long | `i32` | | +//! | unsigned long | `u32` | | +//! | long long | `i64` | | +//! | unsigned long long | `u64` | | +//! | unrestricted float | `f32` | | +//! | float | `Finite<f32>` | | +//! | unrestricted double | `f64` | | +//! | double | `Finite<f64>` | | +//! | DOMString | `DOMString` | | +//! | USVString | `USVString` | | +//! | ByteString | `ByteString` | | +//! | object | `*mut JSObject` | | +//! | interface types | `&T` | `DomRoot<T>` | //! | dictionary types | `&T` | *unsupported* | -//! | enumeration types | `T` | -//! | callback function types | `Rc<T>` | -//! | nullable types | `Option<T>` | -//! | sequences | `Vec<T>` | -//! | union types | `T` | - -use dom::bindings::inheritance::Castable; -use dom::bindings::js::Root; -use dom::bindings::num::Finite; -use dom::bindings::reflector::{DomObject, Reflector}; -use dom::bindings::str::{ByteString, DOMString, USVString}; -use dom::bindings::trace::{JSTraceable, RootedTraceableBox}; -use dom::bindings::utils::DOMClass; -use js; -pub use js::conversions::{FromJSValConvertible, ToJSValConvertible, ConversionResult}; -pub use js::conversions::ConversionBehavior; +//! | enumeration types | `T` | | +//! | callback function types | `Rc<T>` | | +//! | nullable types | `Option<T>` | | +//! | sequences | `Vec<T>` | | +//! | union types | `T` | | + +use crate::dom::bindings::error::{Error, Fallible}; +use crate::dom::bindings::inheritance::Castable; +use crate::dom::bindings::num::Finite; +use crate::dom::bindings::reflector::{DomObject, Reflector}; +use crate::dom::bindings::root::DomRoot; +use crate::dom::bindings::str::{ByteString, DOMString, USVString}; +use crate::dom::bindings::trace::{JSTraceable, RootedTraceableBox}; +use crate::dom::bindings::utils::DOMClass; +use crate::dom::filelist::FileList; +use crate::dom::htmlcollection::HTMLCollection; +use crate::dom::htmlformcontrolscollection::HTMLFormControlsCollection; +use crate::dom::htmloptionscollection::HTMLOptionsCollection; +use crate::dom::nodelist::NodeList; +use crate::dom::windowproxy::WindowProxy; use js::conversions::latin1_to_string; +pub use js::conversions::ConversionBehavior; +pub use js::conversions::{ConversionResult, FromJSValConvertible, ToJSValConvertible}; use js::error::throw_type_error; -use js::glue::{GetProxyPrivate, IsWrapper}; +use js::glue::GetProxyReservedSlot; +use js::glue::JS_GetReservedSlot; +use js::glue::{IsWrapper, UnwrapObjectDynamic}; use js::glue::{RUST_JSID_IS_INT, RUST_JSID_TO_INT}; -use js::glue::{RUST_JSID_IS_STRING, RUST_JSID_TO_STRING, UnwrapObject}; -use js::jsapi::{HandleId, HandleObject, HandleValue, JSContext, JSObject, JSString}; -use js::jsapi::{JS_GetLatin1StringCharsAndLength, JS_GetReservedSlot}; -use js::jsapi::{JS_GetTwoByteStringCharsAndLength, JS_IsArrayObject}; -use js::jsapi::{JS_NewStringCopyN, JS_StringHasLatin1Chars, MutableHandleValue}; -use js::jsval::{ObjectValue, StringValue}; -use js::rust::{ToString, get_object_class, is_dom_class, is_dom_object, maybe_wrap_value}; -use libc; +use js::glue::{RUST_JSID_IS_STRING, RUST_JSID_TO_STRING}; +use js::jsapi::{Heap, JSContext, JSObject, JSString}; +use js::jsapi::{IsWindowProxy, JS_DeprecatedStringHasLatin1Chars, JS_NewStringCopyN}; +use js::jsapi::{ + JS_GetLatin1StringCharsAndLength, JS_GetTwoByteStringCharsAndLength, JS_IsExceptionPending, +}; +use js::jsval::{ObjectValue, StringValue, UndefinedValue}; +use js::rust::wrappers::{IsArrayObject, JS_GetProperty, JS_HasProperty}; +use js::rust::{get_object_class, is_dom_class, is_dom_object, maybe_wrap_value, ToString}; +use js::rust::{HandleId, HandleObject, HandleValue, MutableHandleValue}; use num_traits::Float; use servo_config::opts; -use std::{char, ptr, slice}; +use std::{char, ffi, ptr, slice}; /// A trait to check whether a given `JSObject` implements an IDL interface. pub trait IDLInterface { /// Returns whether the given DOM class derives that interface. - fn derives(&'static DOMClass) -> bool; + fn derives(_: &'static DOMClass) -> bool; } /// A trait to mark an IDL interface as deriving from another one. -#[rustc_on_unimplemented = "The IDL interface `{Self}` is not derived from `{T}`."] pub trait DerivedFrom<T: Castable>: Castable {} impl<T: Float + ToJSValConvertible> ToJSValConvertible for Finite<T> { @@ -76,20 +85,21 @@ impl<T: Float + ToJSValConvertible> ToJSValConvertible for Finite<T> { } } -impl<T: Float + FromJSValConvertible<Config=()>> FromJSValConvertible for Finite<T> { +impl<T: Float + FromJSValConvertible<Config = ()>> FromJSValConvertible for Finite<T> { type Config = (); - unsafe fn from_jsval(cx: *mut JSContext, - value: HandleValue, - option: ()) - -> Result<ConversionResult<Finite<T>>, ()> { - let result = match FromJSValConvertible::from_jsval(cx, value, option) { - Ok(ConversionResult::Success(v)) => v, - Ok(ConversionResult::Failure(error)) => { + unsafe fn from_jsval( + cx: *mut JSContext, + value: HandleValue, + option: (), + ) -> Result<ConversionResult<Finite<T>>, ()> { + let result = match FromJSValConvertible::from_jsval(cx, value, option)? { + ConversionResult::Success(v) => v, + ConversionResult::Failure(error) => { + // FIXME(emilio): Why throwing instead of propagating the error? throw_type_error(cx, &error); return Err(()); - } - _ => return Err(()), + }, }; match Finite::new(result) { Some(v) => Ok(ConversionResult::Success(v)), @@ -101,47 +111,47 @@ impl<T: Float + FromJSValConvertible<Config=()>> FromJSValConvertible for Finite } } -impl <T: DomObject + IDLInterface> FromJSValConvertible for Root<T> { +impl<T: DomObject + IDLInterface> FromJSValConvertible for DomRoot<T> { type Config = (); - unsafe fn from_jsval(_cx: *mut JSContext, - value: HandleValue, - _config: Self::Config) - -> Result<ConversionResult<Root<T>>, ()> { - Ok(match root_from_handlevalue(value) { + unsafe fn from_jsval( + cx: *mut JSContext, + value: HandleValue, + _config: Self::Config, + ) -> Result<ConversionResult<DomRoot<T>>, ()> { + Ok(match root_from_handlevalue(value, cx) { Ok(result) => ConversionResult::Success(result), Err(()) => ConversionResult::Failure("value is not an object".into()), }) } } -impl <T: FromJSValConvertible + JSTraceable> FromJSValConvertible for RootedTraceableBox<T> { - type Config = T::Config; - - unsafe fn from_jsval(cx: *mut JSContext, - value: HandleValue, - config: Self::Config) - -> Result<ConversionResult<Self>, ()> { - T::from_jsval(cx, value, config).map(|result| { - match result { - ConversionResult::Success(v) => ConversionResult::Success(RootedTraceableBox::new(v)), - ConversionResult::Failure(e) => ConversionResult::Failure(e), - } - }) +impl<T: ToJSValConvertible + JSTraceable> ToJSValConvertible for RootedTraceableBox<T> { + #[inline] + unsafe fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { + let value = &**self; + value.to_jsval(cx, rval); } } -/// Convert `id` to a `DOMString`, assuming it is string-valued. -/// -/// Handling of invalid UTF-16 in strings depends on the relevant option. -/// -/// # Panics -/// -/// Panics if `id` is not string-valued. -pub fn string_jsid_to_string(cx: *mut JSContext, id: HandleId) -> DOMString { - unsafe { - assert!(RUST_JSID_IS_STRING(id)); - jsstring_to_str(cx, RUST_JSID_TO_STRING(id)) +impl<T> FromJSValConvertible for RootedTraceableBox<Heap<T>> +where + T: FromJSValConvertible + js::rust::GCMethods + Copy, + Heap<T>: JSTraceable + Default, +{ + type Config = T::Config; + + unsafe fn from_jsval( + cx: *mut JSContext, + value: HandleValue, + config: Self::Config, + ) -> Result<ConversionResult<Self>, ()> { + T::from_jsval(cx, value, config).map(|result| match result { + ConversionResult::Success(inner) => { + ConversionResult::Success(RootedTraceableBox::from_box(Heap::boxed(inner))) + }, + ConversionResult::Failure(msg) => ConversionResult::Failure(msg), + }) } } @@ -150,12 +160,13 @@ pub fn string_jsid_to_string(cx: *mut JSContext, id: HandleId) -> DOMString { /// /// Handling of invalid UTF-16 in strings depends on the relevant option. pub unsafe fn jsid_to_string(cx: *mut JSContext, id: HandleId) -> Option<DOMString> { - if RUST_JSID_IS_STRING(id) { - return Some(jsstring_to_str(cx, RUST_JSID_TO_STRING(id))); + let id_raw = id.into(); + if RUST_JSID_IS_STRING(id_raw) { + return Some(jsstring_to_str(cx, RUST_JSID_TO_STRING(id_raw))); } - if RUST_JSID_IS_INT(id) { - return Some(RUST_JSID_TO_INT(id).to_string().into()); + if RUST_JSID_IS_INT(id_raw) { + return Some(RUST_JSID_TO_INT(id_raw).to_string().into()); } None @@ -169,7 +180,7 @@ impl ToJSValConvertible for USVString { } /// Behavior for stringification of `JSVal`s. -#[derive(PartialEq, Clone)] +#[derive(Clone, PartialEq)] pub enum StringificationBehavior { /// Convert `null` to the string `"null"`. Default, @@ -187,12 +198,12 @@ impl ToJSValConvertible for DOMString { // https://heycam.github.io/webidl/#es-DOMString impl FromJSValConvertible for DOMString { type Config = StringificationBehavior; - unsafe fn from_jsval(cx: *mut JSContext, - value: HandleValue, - null_behavior: StringificationBehavior) - -> Result<ConversionResult<DOMString>, ()> { - if null_behavior == StringificationBehavior::Empty && - value.get().is_null() { + unsafe fn from_jsval( + cx: *mut JSContext, + value: HandleValue, + null_behavior: StringificationBehavior, + ) -> Result<ConversionResult<DOMString>, ()> { + if null_behavior == StringificationBehavior::Empty && value.get().is_null() { Ok(ConversionResult::Success(DOMString::new())) } else { let jsstr = ToString(cx, value); @@ -209,7 +220,7 @@ impl FromJSValConvertible for DOMString { /// Convert the given `JSString` to a `DOMString`. Fails if the string does not /// contain valid UTF-16. pub unsafe fn jsstring_to_str(cx: *mut JSContext, s: *mut JSString) -> DOMString { - let latin1 = JS_StringHasLatin1Chars(s); + let latin1 = JS_DeprecatedStringHasLatin1Chars(s); DOMString::from_string(if latin1 { latin1_to_string(cx, s) } else { @@ -228,16 +239,19 @@ pub unsafe fn jsstring_to_str(cx: *mut JSContext, s: *mut JSString) -> DOMString "Found an unpaired surrogate in a DOM string. \ If you see this in real web content, \ please comment on https://github.com/servo/servo/issues/6564" - } + }; } if opts::get().replace_surrogates { error!(message!()); s.push('\u{FFFD}'); } else { - panic!(concat!(message!(), " Use `-Z replace-surrogates` \ - on the command line to make this non-fatal.")); + panic!(concat!( + message!(), + " Use `-Z replace-surrogates` \ + on the command line to make this non-fatal." + )); } - } + }, } } s @@ -247,33 +261,41 @@ pub unsafe fn jsstring_to_str(cx: *mut JSContext, s: *mut JSString) -> DOMString // http://heycam.github.io/webidl/#es-USVString impl FromJSValConvertible for USVString { type Config = (); - unsafe fn from_jsval(cx: *mut JSContext, value: HandleValue, _: ()) - -> Result<ConversionResult<USVString>, ()> { + unsafe fn from_jsval( + cx: *mut JSContext, + value: HandleValue, + _: (), + ) -> Result<ConversionResult<USVString>, ()> { let jsstr = ToString(cx, value); if jsstr.is_null() { debug!("ToString failed"); return Err(()); } - let latin1 = JS_StringHasLatin1Chars(jsstr); + let latin1 = JS_DeprecatedStringHasLatin1Chars(jsstr); if latin1 { // FIXME(ajeffrey): Convert directly from DOMString to USVString - return Ok(ConversionResult::Success( - USVString(String::from(jsstring_to_str(cx, jsstr))))); + return Ok(ConversionResult::Success(USVString(String::from( + jsstring_to_str(cx, jsstr), + )))); } 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(ConversionResult::Success(USVString(String::from_utf16_lossy(char_vec)))) + Ok(ConversionResult::Success(USVString( + String::from_utf16_lossy(char_vec), + ))) } } // http://heycam.github.io/webidl/#es-ByteString impl ToJSValConvertible for ByteString { - unsafe fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { - let jsstr = JS_NewStringCopyN(cx, - self.as_ptr() as *const libc::c_char, - self.len() as libc::size_t); + unsafe fn to_jsval(&self, cx: *mut JSContext, mut rval: MutableHandleValue) { + 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"); } @@ -284,24 +306,27 @@ impl ToJSValConvertible for ByteString { // http://heycam.github.io/webidl/#es-ByteString impl FromJSValConvertible for ByteString { type Config = (); - unsafe fn from_jsval(cx: *mut JSContext, - value: HandleValue, - _option: ()) - -> Result<ConversionResult<ByteString>, ()> { + unsafe fn from_jsval( + cx: *mut JSContext, + value: HandleValue, + _option: (), + ) -> Result<ConversionResult<ByteString>, ()> { let string = ToString(cx, value); if string.is_null() { debug!("ToString failed"); return Err(()); } - let latin1 = JS_StringHasLatin1Chars(string); + let latin1 = JS_DeprecatedStringHasLatin1Chars(string); if latin1 { let mut length = 0; let chars = JS_GetLatin1StringCharsAndLength(cx, ptr::null(), string, &mut length); assert!(!chars.is_null()); let char_slice = slice::from_raw_parts(chars as *mut u8, length as usize); - return Ok(ConversionResult::Success(ByteString::new(char_slice.to_vec()))); + return Ok(ConversionResult::Success(ByteString::new( + char_slice.to_vec(), + ))); } let mut length = 0; @@ -312,15 +337,15 @@ impl FromJSValConvertible for ByteString { throw_type_error(cx, "Invalid ByteString"); Err(()) } else { - Ok(ConversionResult::Success( - ByteString::new(char_vec.iter().map(|&c| c as u8).collect()))) + Ok(ConversionResult::Success(ByteString::new( + char_vec.iter().map(|&c| c as u8).collect(), + ))) } } } - impl ToJSValConvertible for Reflector { - unsafe fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { + unsafe fn to_jsval(&self, cx: *mut JSContext, mut rval: MutableHandleValue) { let obj = self.get_jsobject().get(); assert!(!obj.is_null()); rval.set(ObjectValue(obj)); @@ -345,11 +370,12 @@ pub const DOM_OBJECT_SLOT: u32 = 0; /// Get the private pointer of a DOM object from a given reflector. pub unsafe fn private_from_object(obj: *mut JSObject) -> *const libc::c_void { - let value = if is_dom_object(obj) { - JS_GetReservedSlot(obj, DOM_OBJECT_SLOT) + let mut value = UndefinedValue(); + if is_dom_object(obj) { + JS_GetReservedSlot(obj, DOM_OBJECT_SLOT, &mut value); } else { debug_assert!(is_dom_proxy(obj)); - GetProxyPrivate(obj) + GetProxyReservedSlot(obj, 0, &mut value); }; if value.is_undefined() { ptr::null() @@ -360,7 +386,7 @@ pub unsafe fn private_from_object(obj: *mut JSObject) -> *const libc::c_void { /// Get the `DOMClass` from `obj`, or `Err(())` if `obj` is not a DOM object. pub unsafe fn get_dom_class(obj: *mut JSObject) -> Result<&'static DOMClass, ()> { - use dom::bindings::utils::DOMJSClass; + use crate::dom::bindings::utils::DOMJSClass; use js::glue::GetProxyHandlerExtra; let clasp = get_object_class(obj); @@ -378,6 +404,11 @@ pub unsafe fn get_dom_class(obj: *mut JSObject) -> Result<&'static DOMClass, ()> Err(()) } +pub(crate) enum PrototypeCheck { + Derive(fn(&'static DOMClass) -> bool), + Depth { depth: usize, proto_id: u16 }, +} + /// Get a `*const libc::c_void` for the given DOM object, unwrapping any /// wrapper around it first, and checking if the object is of the correct type. /// @@ -385,15 +416,15 @@ pub unsafe fn get_dom_class(obj: *mut JSObject) -> Result<&'static DOMClass, ()> /// not an object for a DOM object of the given type (as defined by the /// proto_id and proto_depth). #[inline] -pub unsafe fn private_from_proto_check<F>(mut obj: *mut JSObject, - proto_check: F) - -> Result<*const libc::c_void, ()> - where F: Fn(&'static DOMClass) -> bool -{ - let dom_class = try!(get_dom_class(obj).or_else(|_| { +pub(crate) unsafe fn private_from_proto_check( + mut obj: *mut JSObject, + cx: *mut JSContext, + proto_check: PrototypeCheck, +) -> Result<*const libc::c_void, ()> { + let dom_class = get_dom_class(obj).or_else(|_| { if IsWrapper(obj) { trace!("found wrapper"); - obj = UnwrapObject(obj, /* stopAtWindowProxy = */ 0); + obj = UnwrapObjectDynamic(obj, cx, /* stopAtWindowProxy = */ 0); if obj.is_null() { trace!("unwrapping security wrapper failed"); Err(()) @@ -406,8 +437,35 @@ pub unsafe fn private_from_proto_check<F>(mut obj: *mut JSObject, trace!("not a dom wrapper"); Err(()) } - })); + })?; + + let prototype_matches = match proto_check { + PrototypeCheck::Derive(f) => (f)(dom_class), + PrototypeCheck::Depth { depth, proto_id } => { + dom_class.interface_chain[depth] as u16 == proto_id + }, + }; + + if prototype_matches { + trace!("good prototype"); + Ok(private_from_object(obj)) + } else { + trace!("bad prototype"); + Err(()) + } +} +/// Get a `*const libc::c_void` for the given DOM object, unless it is a DOM +/// wrapper, and checking if the object is of the correct type. +/// +/// Returns Err(()) if `obj` is a wrapper or if the object is not an object +/// for a DOM object of the given type (as defined by the proto_id and proto_depth). +#[inline] +unsafe fn private_from_proto_check_static( + obj: *mut JSObject, + proto_check: fn(&'static DOMClass) -> bool, +) -> Result<*const libc::c_void, ()> { + let dom_class = get_dom_class(obj).map_err(|_| ())?; if proto_check(dom_class) { trace!("good prototype"); Ok(private_from_object(obj)) @@ -418,66 +476,189 @@ pub unsafe fn private_from_proto_check<F>(mut obj: *mut JSObject, } /// Get a `*const T` for a DOM object accessible from a `JSObject`. -pub fn native_from_object<T>(obj: *mut JSObject) -> Result<*const T, ()> - where T: DomObject + IDLInterface +pub fn native_from_object<T>(obj: *mut JSObject, cx: *mut JSContext) -> Result<*const T, ()> +where + T: DomObject + IDLInterface, { unsafe { - private_from_proto_check(obj, T::derives).map(|ptr| ptr as *const T) + private_from_proto_check(obj, cx, PrototypeCheck::Derive(T::derives)) + .map(|ptr| ptr as *const T) } } -/// Get a `Root<T>` for the given DOM object, unwrapping any wrapper +/// Get a `*const T` for a DOM object accessible from a `JSObject`, where the DOM object +/// is guaranteed not to be a wrapper. +pub fn native_from_object_static<T>(obj: *mut JSObject) -> Result<*const T, ()> +where + T: DomObject + IDLInterface, +{ + unsafe { private_from_proto_check_static(obj, T::derives).map(|ptr| ptr as *const T) } +} + +/// Get a `DomRoot<T>` for the given DOM object, unwrapping any wrapper +/// around it first, and checking if the object is of the correct type. +/// +/// 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 root_from_object<T>(obj: *mut JSObject, cx: *mut JSContext) -> Result<DomRoot<T>, ()> +where + T: DomObject + IDLInterface, +{ + native_from_object(obj, cx).map(|ptr| unsafe { DomRoot::from_ref(&*ptr) }) +} + +/// Get a `DomRoot<T>` for the given DOM object, unwrapping any wrapper /// around it first, and checking if the object is of the correct type. /// /// 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 root_from_object<T>(obj: *mut JSObject) -> Result<Root<T>, ()> - where T: DomObject + IDLInterface +pub fn root_from_object_static<T>(obj: *mut JSObject) -> Result<DomRoot<T>, ()> +where + T: DomObject + IDLInterface, { - native_from_object(obj).map(|ptr| unsafe { Root::from_ref(&*ptr) }) + native_from_object_static(obj).map(|ptr| unsafe { DomRoot::from_ref(&*ptr) }) } /// Get a `*const T` for a DOM object accessible from a `HandleValue`. /// Caller is responsible for throwing a JS exception if needed in case of error. -pub fn native_from_handlevalue<T>(v: HandleValue) -> Result<*const T, ()> - where T: DomObject + IDLInterface +pub fn native_from_handlevalue<T>(v: HandleValue, cx: *mut JSContext) -> Result<*const T, ()> +where + T: DomObject + IDLInterface, { if !v.get().is_object() { return Err(()); } - native_from_object(v.get().to_object()) + native_from_object(v.get().to_object(), cx) } -/// Get a `Root<T>` for a DOM object accessible from a `HandleValue`. +/// Get a `DomRoot<T>` for a DOM object accessible from a `HandleValue`. /// Caller is responsible for throwing a JS exception if needed in case of error. -pub fn root_from_handlevalue<T>(v: HandleValue) -> Result<Root<T>, ()> - where T: DomObject + IDLInterface +pub fn root_from_handlevalue<T>(v: HandleValue, cx: *mut JSContext) -> Result<DomRoot<T>, ()> +where + T: DomObject + IDLInterface, { if !v.get().is_object() { return Err(()); } - root_from_object(v.get().to_object()) + root_from_object(v.get().to_object(), cx) } -/// Get a `Root<T>` for a DOM object accessible from a `HandleObject`. -pub fn root_from_handleobject<T>(obj: HandleObject) -> Result<Root<T>, ()> - where T: DomObject + IDLInterface +/// Get a `DomRoot<T>` for a DOM object accessible from a `HandleObject`. +pub fn root_from_handleobject<T>(obj: HandleObject, cx: *mut JSContext) -> Result<DomRoot<T>, ()> +where + T: DomObject + IDLInterface, { - root_from_object(obj.get()) + root_from_object(obj.get(), cx) } -impl<T: DomObject> ToJSValConvertible for Root<T> { +impl<T: DomObject> ToJSValConvertible for DomRoot<T> { unsafe fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { self.reflector().to_jsval(cx, rval); } } -/// Returns whether `value` is an array-like object. -/// Note: Currently only Arrays are supported. -/// TODO: Expand this to support sequences and other array-like objects +/// Returns whether `value` is an array-like object (Array, FileList, +/// HTMLCollection, HTMLFormControlsCollection, HTMLOptionsCollection, +/// NodeList). pub unsafe fn is_array_like(cx: *mut JSContext, value: HandleValue) -> bool { - let mut result = false; - assert!(JS_IsArrayObject(cx, value, &mut result)); - result + let mut is_array = false; + assert!(IsArrayObject(cx, value, &mut is_array)); + if is_array { + return true; + } + + let object: *mut JSObject = match FromJSValConvertible::from_jsval(cx, value, ()).unwrap() { + ConversionResult::Success(object) => object, + _ => return false, + }; + + if root_from_object::<FileList>(object, cx).is_ok() { + return true; + } + if root_from_object::<HTMLCollection>(object, cx).is_ok() { + return true; + } + if root_from_object::<HTMLFormControlsCollection>(object, cx).is_ok() { + return true; + } + if root_from_object::<HTMLOptionsCollection>(object, cx).is_ok() { + return true; + } + if root_from_object::<NodeList>(object, cx).is_ok() { + return true; + } + + false +} + +/// Get a property from a JS object. +pub unsafe fn get_property_jsval( + cx: *mut JSContext, + object: HandleObject, + name: &str, + mut rval: MutableHandleValue, +) -> Fallible<()> { + rval.set(UndefinedValue()); + let cname = match ffi::CString::new(name) { + Ok(cname) => cname, + Err(_) => return Ok(()), + }; + let mut found = false; + if JS_HasProperty(cx, object, cname.as_ptr(), &mut found) && found { + JS_GetProperty(cx, object, cname.as_ptr(), rval); + if JS_IsExceptionPending(cx) { + return Err(Error::JSFailed); + } + Ok(()) + } else if JS_IsExceptionPending(cx) { + Err(Error::JSFailed) + } else { + Ok(()) + } +} + +/// Get a property from a JS object, and convert it to a Rust value. +pub unsafe fn get_property<T>( + cx: *mut JSContext, + object: HandleObject, + name: &str, + option: T::Config, +) -> Fallible<Option<T>> +where + T: FromJSValConvertible, +{ + debug!("Getting property {}.", name); + rooted!(in(cx) let mut result = UndefinedValue()); + get_property_jsval(cx, object, name, result.handle_mut())?; + if result.is_undefined() { + debug!("No property {}.", name); + return Ok(None); + } + debug!("Converting property {}.", name); + match T::from_jsval(cx, result.handle(), option) { + Ok(ConversionResult::Success(value)) => Ok(Some(value)), + Ok(ConversionResult::Failure(_)) => Ok(None), + Err(()) => Err(Error::JSFailed), + } +} + +/// Get a `DomRoot<T>` for a WindowProxy accessible from a `HandleValue`. +/// Caller is responsible for throwing a JS exception if needed in case of error. +pub unsafe fn windowproxy_from_handlevalue( + v: HandleValue, + _cx: *mut JSContext, +) -> Result<DomRoot<WindowProxy>, ()> { + if !v.get().is_object() { + return Err(()); + } + let object = v.get().to_object(); + if !IsWindowProxy(object) { + return Err(()); + } + let mut value = UndefinedValue(); + GetProxyReservedSlot(object, 0, &mut value); + let ptr = value.to_private() as *const WindowProxy; + Ok(DomRoot::from_ref(&*ptr)) } |