diff --git a/components/plugins/reflector.rs b/components/plugins/reflector.rs index 027f2ecf172a..84b960168d03 100644 --- a/components/plugins/reflector.rs +++ b/components/plugins/reflector.rs @@ -48,6 +48,19 @@ pub fn expand_reflector(cx: &mut ExtCtxt, span: Span, _: &MetaItem, annotatable: impl_item.map(|it| push(Annotatable::Item(it))) } }; + + let impl_item = quote_item!(cx, + impl ::js::conversions::ToJSValConvertible for $struct_name { + #[allow(unsafe_code)] + unsafe fn to_jsval(&self, + cx: *mut ::js::jsapi::JSContext, + rval: ::js::jsapi::MutableHandleValue) { + let object = ::dom::bindings::reflector::Reflectable::reflector(self).get_jsobject(); + object.to_jsval(cx, rval) + } + } + ); + impl_item.map(|it| push(Annotatable::Item(it))); } else { cx.span_err(span, "#[dom_struct] seems to have been applied to a non-struct"); } diff --git a/components/script/devtools.rs b/components/script/devtools.rs index b12619b51b08..3d1bc95c2de3 100644 --- a/components/script/devtools.rs +++ b/components/script/devtools.rs @@ -28,33 +28,37 @@ use uuid::Uuid; #[allow(unsafe_code)] pub fn handle_evaluate_js(global: &GlobalRef, eval: String, reply: IpcSender) { - let cx = global.get_cx(); - let mut rval = RootedValue::new(cx, UndefinedValue()); - global.evaluate_js_on_global_with_result(&eval, rval.handle_mut()); - - reply.send(if rval.ptr.is_undefined() { - EvaluateJSReply::VoidValue - } else if rval.ptr.is_boolean() { - EvaluateJSReply::BooleanValue(rval.ptr.to_boolean()) - } else if rval.ptr.is_double() || rval.ptr.is_int32() { - EvaluateJSReply::NumberValue( - FromJSValConvertible::from_jsval(cx, rval.handle(), ()).unwrap()) - } else if rval.ptr.is_string() { - EvaluateJSReply::StringValue(jsstring_to_str(cx, rval.ptr.to_string()).0) - } else if rval.ptr.is_null() { - EvaluateJSReply::NullValue - } else { - assert!(rval.ptr.is_object()); - - let obj = RootedObject::new(cx, rval.ptr.to_object()); - let class_name = unsafe { CStr::from_ptr(ObjectClassName(cx, obj.handle())) }; - let class_name = str::from_utf8(class_name.to_bytes()).unwrap(); - - EvaluateJSReply::ActorValue { - class: class_name.to_owned(), - uuid: Uuid::new_v4().to_string(), + // global.get_cx() returns a valid `JSContext` pointer, so this is safe. + let result = unsafe { + let cx = global.get_cx(); + let mut rval = RootedValue::new(cx, UndefinedValue()); + global.evaluate_js_on_global_with_result(&eval, rval.handle_mut()); + + if rval.ptr.is_undefined() { + EvaluateJSReply::VoidValue + } else if rval.ptr.is_boolean() { + EvaluateJSReply::BooleanValue(rval.ptr.to_boolean()) + } else if rval.ptr.is_double() || rval.ptr.is_int32() { + EvaluateJSReply::NumberValue( + FromJSValConvertible::from_jsval(cx, rval.handle(), ()).unwrap()) + } else if rval.ptr.is_string() { + EvaluateJSReply::StringValue(jsstring_to_str(cx, rval.ptr.to_string()).0) + } else if rval.ptr.is_null() { + EvaluateJSReply::NullValue + } else { + assert!(rval.ptr.is_object()); + + let obj = RootedObject::new(cx, rval.ptr.to_object()); + let class_name = CStr::from_ptr(ObjectClassName(cx, obj.handle())); + let class_name = str::from_utf8(class_name.to_bytes()).unwrap(); + + EvaluateJSReply::ActorValue { + class: class_name.to_owned(), + uuid: Uuid::new_v4().to_string(), + } } - }).unwrap(); + }; + reply.send(result).unwrap(); } pub fn handle_get_root_node(page: &Rc, pipeline: PipelineId, reply: IpcSender) { diff --git a/components/script/dom/bindings/codegen/CodegenRust.py b/components/script/dom/bindings/codegen/CodegenRust.py index fbe2c46e9ded..14a26268efd3 100644 --- a/components/script/dom/bindings/codegen/CodegenRust.py +++ b/components/script/dom/bindings/codegen/CodegenRust.py @@ -3413,7 +3413,7 @@ def __init__(self, enum): ]; impl ToJSValConvertible for super::%s { - fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { + unsafe fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { strings[*self as usize].to_jsval(cx, rval); } } @@ -3534,7 +3534,7 @@ def define(self): } impl ToJSValConvertible for %s { - fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { + unsafe fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { match *self { %s } @@ -3644,12 +3644,12 @@ def get_match(name): names.append(name) conversions.append(CGGeneric( - "unsafe { throw_not_in_union(cx, \"%s\"); }\n" + "throw_not_in_union(cx, \"%s\");\n" "Err(())" % ", ".join(names))) method = CGWrapper( CGIndenter(CGList(conversions, "\n\n")), - pre="fn from_jsval(cx: *mut JSContext,\n" - " value: HandleValue, _option: ()) -> Result<%s, ()> {\n" % self.type, + pre="unsafe fn from_jsval(cx: *mut JSContext,\n" + " value: HandleValue, _option: ()) -> Result<%s, ()> {\n" % self.type, post="\n}") return CGWrapper( CGIndenter(CGList([ @@ -3666,7 +3666,7 @@ def try_method(self, t): return CGWrapper( CGIndenter(jsConversion, 4), - pre="fn TryConvertTo%s(cx: *mut JSContext, value: HandleValue) -> %s {\n" % (t.name, returnType), + pre="unsafe fn TryConvertTo%s(cx: *mut JSContext, value: HandleValue) -> %s {\n" % (t.name, returnType), post="\n}") def define(self): @@ -4967,8 +4967,8 @@ def memberInsert(memberInfo): "}\n" "\n" "impl ToJSValConvertible for ${selfName} {\n" - " fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) {\n" - " let obj = unsafe { RootedObject::new(cx, JS_NewObject(cx, ptr::null())) };\n" + " unsafe fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) {\n" + " let obj = RootedObject::new(cx, JS_NewObject(cx, ptr::null()));\n" "${insertMembers}" " rval.set(ObjectOrNullValue(obj.ptr))\n" " }\n" @@ -5459,7 +5459,7 @@ def __init__(self, callback): } impl ToJSValConvertible for ${type} { - fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { + unsafe fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { self.callback().to_jsval(cx, rval); } }\ @@ -5563,10 +5563,10 @@ def getImpl(self): "${convertArgs}" "${doCall}" "${returnResult}").substitute(replacements) - return CGList([ + return CGWrapper(CGIndenter(CGList([ CGGeneric(pre), CGGeneric(body), - ], "\n").define() + ], "\n"), 4), pre="unsafe {\n", post="\n}").define() def getResultConversion(self): replacements = { @@ -5709,15 +5709,13 @@ def getCall(self): replacements["argc"] = "0" return string.Template( "${getCallable}" - "let ok = unsafe {\n" - " let rootedThis = RootedObject::new(cx, ${thisObj});\n" - " JS_CallFunctionValue(\n" - " cx, rootedThis.handle(), callable.handle(),\n" - " &HandleValueArray {\n" - " length_: ${argc} as ::libc::size_t,\n" - " elements_: ${argv}\n" - " }, rval.handle_mut())\n" - "};\n" + "let rootedThis = RootedObject::new(cx, ${thisObj});\n" + "let ok = JS_CallFunctionValue(\n" + " cx, rootedThis.handle(), callable.handle(),\n" + " &HandleValueArray {\n" + " length_: ${argc} as ::libc::size_t,\n" + " elements_: ${argv}\n" + " }, rval.handle_mut());\n" "if !ok {\n" " return Err(JSFailed);\n" "}\n").substitute(replacements) @@ -5732,7 +5730,7 @@ def getThisObj(self): return "aThisObj.get()" def getCallableDecl(self): - return "let callable = RootedValue::new(cx, ObjectValue(unsafe {&*self.parent.callback()}));\n" + return "let callable = RootedValue::new(cx, ObjectValue(&*self.parent.callback()));\n" class CallbackOperationBase(CallbackMethod): @@ -5762,11 +5760,11 @@ def getCallableDecl(self): if not self.singleOperation: return 'JS::Rooted callable(cx);\n' + getCallableFromProp return ( - 'let isCallable = unsafe { IsCallable(self.parent.callback()) };\n' + 'let isCallable = IsCallable(self.parent.callback());\n' 'let callable =\n' + CGIndenter( CGIfElseWrapper('isCallable', - CGGeneric('unsafe { RootedValue::new(cx, ObjectValue(&*self.parent.callback())) }'), + CGGeneric('RootedValue::new(cx, ObjectValue(&*self.parent.callback()))'), CGGeneric(getCallableFromProp))).define() + ';\n') diff --git a/components/script/dom/bindings/conversions.rs b/components/script/dom/bindings/conversions.rs index 001d10e574bd..dcc1f164d25f 100644 --- a/components/script/dom/bindings/conversions.rs +++ b/components/script/dom/bindings/conversions.rs @@ -39,26 +39,23 @@ use dom::bindings::reflector::{Reflectable, Reflector}; use dom::bindings::str::{ByteString, USVString}; use dom::bindings::utils::DOMClass; use js; +pub use js::conversions::{FromJSValConvertible, ToJSValConvertible, ConversionBehavior}; use js::error::throw_type_error; -use js::glue::{GetProxyPrivate, IsWrapper, RUST_JS_NumberValue}; +use js::glue::{GetProxyPrivate, IsWrapper}; use js::glue::{RUST_JSID_IS_STRING, RUST_JSID_TO_STRING, UnwrapObject}; use js::jsapi::{HandleId, HandleObject, HandleValue, JS_GetClass}; -use js::jsapi::{JSClass, JSContext, JSObject, JSString, MutableHandleValue}; +use js::jsapi::{JSClass, JSContext, JSObject, MutableHandleValue}; use js::jsapi::{JS_GetLatin1StringCharsAndLength, JS_GetReservedSlot}; use js::jsapi::{JS_GetTwoByteStringCharsAndLength, JS_NewStringCopyN}; -use js::jsapi::{JS_NewArrayObject1, JS_DefineElement, RootedValue, RootedObject}; -use js::jsapi::{JS_NewUCStringCopyN, JS_StringHasLatin1Chars, JS_WrapValue}; -use js::jsval::{BooleanValue, Int32Value, NullValue, UInt32Value, UndefinedValue}; -use js::jsval::{JSVal, ObjectOrNullValue, ObjectValue, StringValue}; -use js::rust::{ToBoolean, ToNumber, ToString, ToUint16}; -use js::rust::{ToInt32, ToUint32}; -use js::rust::{ToInt64, ToUint64}; +use js::jsapi::{JS_StringHasLatin1Chars, JS_WrapValue}; +use js::jsval::{ObjectValue, StringValue}; +use js::rust::ToString; use libc; use num::Float; -use num::traits::{Bounded, Zero}; -use std::rc::Rc; -use std::{char, ptr, slice}; -use util::str::DOMString; +use std::{ptr, slice}; +use util::str::{DOMString}; +pub use util::str::{StringificationBehavior, jsstring_to_str}; + trait As: Copy { fn cast(self) -> O; @@ -111,290 +108,9 @@ pub trait IDLInterface { #[rustc_on_unimplemented = "The IDL interface `{Self}` is not derived from `{T}`."] pub trait DerivedFrom: Castable {} -/// 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, rval: MutableHandleValue); -} - -/// A trait to convert `JSVal`s to Rust types. -pub trait FromJSValConvertible: Sized { - /// Optional configurable behaviour switch; use () for no configuration. - type Config; - /// Convert `val` to type `Self`. - /// 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: HandleValue, option: Self::Config) -> Result; -} - -/// Behavior for converting out-of-range integers. -#[derive(PartialEq, Eq)] -pub enum ConversionBehavior { - /// Wrap into the integer's range. - Default, - /// Throw an exception. - EnforceRange, - /// Clamp into the integer's range. - Clamp -} - -/// Try to cast the number to a smaller type, but -/// if it doesn't fit, it will return an error. -fn enforce_range(cx: *mut JSContext, d: f64) -> Result - where D: Bounded + As, - f64: As -{ - if d.is_infinite() { - unsafe { throw_type_error(cx, "value out of range in an EnforceRange argument") }; - return Err(()); - } - - let rounded = d.round(); - if D::min_value().cast() <= rounded && rounded <= D::max_value().cast() { - Ok(rounded.cast()) - } else { - unsafe { throw_type_error(cx, "value out of range in an EnforceRange argument") }; - Err(()) - } -} - -/// Try to cast the number to a smaller type, but if it doesn't fit, -/// round it to the MAX or MIN of the source type before casting it to -/// the destination type. -fn clamp_to(d: f64) -> D - where D: Bounded + As + Zero, - f64: As -{ - if d.is_nan() { - D::zero() - } else if d > D::max_value().cast() { - D::max_value() - } else if d < D::min_value().cast() { - D::min_value() - } else { - d.cast() - } -} - -//http://heycam.github.io/webidl/#es-void -impl ToJSValConvertible for () { - fn to_jsval(&self, _cx: *mut JSContext, rval: MutableHandleValue) { - rval.set(UndefinedValue()); - } -} - -impl ToJSValConvertible for JSVal { - fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { - rval.set(*self); - if unsafe { !JS_WrapValue(cx, rval) } { - panic!("JS_WrapValue failed."); - } - } -} - -impl ToJSValConvertible for HandleValue { - fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { - rval.set(self.get()); - if unsafe { !JS_WrapValue(cx, rval) } { - panic!("JS_WrapValue failed."); - } - } -} - -#[inline] -unsafe fn convert_int_from_jsval(cx: *mut JSContext, value: HandleValue, - option: ConversionBehavior, - convert_fn: unsafe fn(*mut JSContext, HandleValue) -> Result) - -> Result - where T: Bounded + Zero + As, - M: Zero + As, - f64: As -{ - match option { - ConversionBehavior::Default => Ok(try!(convert_fn(cx, value)).cast()), - ConversionBehavior::EnforceRange => enforce_range(cx, try!(ToNumber(cx, value))), - ConversionBehavior::Clamp => Ok(clamp_to(try!(ToNumber(cx, value)))), - } -} - -//http://heycam.github.io/webidl/#es-boolean -impl ToJSValConvertible for bool { - fn to_jsval(&self, _cx: *mut JSContext, rval: MutableHandleValue) { - rval.set(BooleanValue(*self)); - } -} - -//http://heycam.github.io/webidl/#es-boolean -impl FromJSValConvertible for bool { - type Config = (); - fn from_jsval(_cx: *mut JSContext, val: HandleValue, _option: ()) -> Result { - Ok(unsafe { ToBoolean(val) }) - } -} - -//http://heycam.github.io/webidl/#es-byte -impl ToJSValConvertible for i8 { - fn to_jsval(&self, _cx: *mut JSContext, rval: MutableHandleValue) { - rval.set(Int32Value(*self as i32)); - } -} - -//http://heycam.github.io/webidl/#es-byte -impl FromJSValConvertible for i8 { - type Config = ConversionBehavior; - fn from_jsval(cx: *mut JSContext, val: HandleValue, option: ConversionBehavior) -> Result { - unsafe { convert_int_from_jsval(cx, val, option, ToInt32) } - } -} - -//http://heycam.github.io/webidl/#es-octet -impl ToJSValConvertible for u8 { - fn to_jsval(&self, _cx: *mut JSContext, rval: MutableHandleValue) { - rval.set(Int32Value(*self as i32)); - } -} - -//http://heycam.github.io/webidl/#es-octet -impl FromJSValConvertible for u8 { - type Config = ConversionBehavior; - fn from_jsval(cx: *mut JSContext, val: HandleValue, option: ConversionBehavior) -> Result { - unsafe { convert_int_from_jsval(cx, val, option, ToInt32) } - } -} - -//http://heycam.github.io/webidl/#es-short -impl ToJSValConvertible for i16 { - fn to_jsval(&self, _cx: *mut JSContext, rval: MutableHandleValue) { - rval.set(Int32Value(*self as i32)); - } -} - -//http://heycam.github.io/webidl/#es-short -impl FromJSValConvertible for i16 { - type Config = ConversionBehavior; - fn from_jsval(cx: *mut JSContext, val: HandleValue, option: ConversionBehavior) -> Result { - unsafe { convert_int_from_jsval(cx, val, option, ToInt32) } - } -} - -//http://heycam.github.io/webidl/#es-unsigned-short -impl ToJSValConvertible for u16 { - fn to_jsval(&self, _cx: *mut JSContext, rval: MutableHandleValue) { - rval.set(Int32Value(*self as i32)); - } -} - -//http://heycam.github.io/webidl/#es-unsigned-short -impl FromJSValConvertible for u16 { - type Config = ConversionBehavior; - fn from_jsval(cx: *mut JSContext, val: HandleValue, option: ConversionBehavior) -> Result { - unsafe { convert_int_from_jsval(cx, val, option, ToUint16) } - } -} - -//http://heycam.github.io/webidl/#es-long -impl ToJSValConvertible for i32 { - fn to_jsval(&self, _cx: *mut JSContext, rval: MutableHandleValue) { - rval.set(Int32Value(*self)); - } -} - -//http://heycam.github.io/webidl/#es-long -impl FromJSValConvertible for i32 { - type Config = ConversionBehavior; - fn from_jsval(cx: *mut JSContext, val: HandleValue, option: ConversionBehavior) -> Result { - unsafe { convert_int_from_jsval(cx, val, option, ToInt32) } - } -} - -//http://heycam.github.io/webidl/#es-unsigned-long -impl ToJSValConvertible for u32 { - fn to_jsval(&self, _cx: *mut JSContext, rval: MutableHandleValue) { - rval.set(UInt32Value(*self)); - } -} - -//http://heycam.github.io/webidl/#es-unsigned-long -impl FromJSValConvertible for u32 { - type Config = ConversionBehavior; - fn from_jsval(cx: *mut JSContext, val: HandleValue, option: ConversionBehavior) -> Result { - unsafe { convert_int_from_jsval(cx, val, option, ToUint32) } - } -} - -//http://heycam.github.io/webidl/#es-long-long -impl ToJSValConvertible for i64 { - fn to_jsval(&self, _cx: *mut JSContext, rval: MutableHandleValue) { - unsafe { - rval.set(RUST_JS_NumberValue(*self as f64)); - } - } -} - -//http://heycam.github.io/webidl/#es-long-long -impl FromJSValConvertible for i64 { - type Config = ConversionBehavior; - fn from_jsval(cx: *mut JSContext, val: HandleValue, option: ConversionBehavior) -> Result { - unsafe { convert_int_from_jsval(cx, val, option, ToInt64) } - } -} - -//http://heycam.github.io/webidl/#es-unsigned-long-long -impl ToJSValConvertible for u64 { - fn to_jsval(&self, _cx: *mut JSContext, rval: MutableHandleValue) { - unsafe { - rval.set(RUST_JS_NumberValue(*self as f64)); - } - } -} - -//http://heycam.github.io/webidl/#es-unsigned-long-long -impl FromJSValConvertible for u64 { - type Config = ConversionBehavior; - fn from_jsval(cx: *mut JSContext, val: HandleValue, option: ConversionBehavior) -> Result { - unsafe { convert_int_from_jsval(cx, val, option, ToUint64) } - } -} - -//http://heycam.github.io/webidl/#es-float -impl ToJSValConvertible for f32 { - fn to_jsval(&self, _cx: *mut JSContext, rval: MutableHandleValue) { - unsafe { - rval.set(RUST_JS_NumberValue(*self as f64)); - } - } -} - -//http://heycam.github.io/webidl/#es-float -impl FromJSValConvertible for f32 { - type Config = (); - fn from_jsval(cx: *mut JSContext, val: HandleValue, _option: ()) -> Result { - let result = unsafe { ToNumber(cx, val) }; - result.map(|f| f as f32) - } -} - -//http://heycam.github.io/webidl/#es-double -impl ToJSValConvertible for f64 { - fn to_jsval(&self, _cx: *mut JSContext, rval: MutableHandleValue) { - unsafe { - rval.set(RUST_JS_NumberValue(*self)); - } - } -} - -//http://heycam.github.io/webidl/#es-double -impl FromJSValConvertible for f64 { - type Config = (); - fn from_jsval(cx: *mut JSContext, val: HandleValue, _option: ()) -> Result { - unsafe { ToNumber(cx, val) } - } -} - impl ToJSValConvertible for Finite { #[inline] - fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { + unsafe fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { let value = **self; value.to_jsval(cx, rval); } @@ -403,109 +119,18 @@ impl ToJSValConvertible for Finite { impl> FromJSValConvertible for Finite { type Config = (); - fn from_jsval(cx: *mut JSContext, value: HandleValue, option: ()) -> Result, ()> { + unsafe fn from_jsval(cx: *mut JSContext, value: HandleValue, option: ()) -> Result, ()> { let result = try!(FromJSValConvertible::from_jsval(cx, value, option)); match Finite::new(result) { Some(v) => Ok(v), None => { - unsafe { throw_type_error(cx, "this argument is not a finite floating-point value") }; + throw_type_error(cx, "this argument is not a finite floating-point value"); Err(()) }, } } } -impl ToJSValConvertible for str { - fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { - let mut string_utf16: Vec = Vec::with_capacity(self.len()); - unsafe { - string_utf16.extend(self.utf16_units()); - let jsstr = JS_NewUCStringCopyN(cx, string_utf16.as_ptr(), - string_utf16.len() as libc::size_t); - if jsstr.is_null() { - panic!("JS_NewUCStringCopyN failed"); - } - rval.set(StringValue(&*jsstr)); - } - } -} - -//http://heycam.github.io/webidl/#es-DOMString -impl ToJSValConvertible for String { - fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { - (**self).to_jsval(cx, rval); - } -} - -//http://heycam.github.io/webidl/#es-DOMString -impl ToJSValConvertible for DOMString { - fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { - (**self).to_jsval(cx, rval); - } -} - -/// Behavior for stringification of `JSVal`s. -#[derive(PartialEq)] -pub enum StringificationBehavior { - /// Convert `null` to the string `"null"`. - Default, - /// Convert `null` to the empty string. - Empty, -} - -/// 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 { - let mut length = 0; - let latin1 = unsafe { JS_StringHasLatin1Chars(s) }; - DOMString(if latin1 { - let chars = unsafe { - JS_GetLatin1StringCharsAndLength(cx, ptr::null(), s, &mut length) - }; - assert!(!chars.is_null()); - - 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 potentially_ill_formed_utf16 = unsafe { - slice::from_raw_parts(chars as *const u16, length as usize) - }; - let mut s = String::with_capacity(length as usize); - for item in char::decode_utf16(potentially_ill_formed_utf16.iter().cloned()) { - match item { - Ok(c) => s.push(c), - Err(_) => { - // FIXME: Add more info like document URL in the message? - macro_rules! message { - () => { - "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 ::util::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.")); - } - } - } - } - s - }) -} - /// 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: HandleId) -> DOMString { @@ -515,30 +140,9 @@ pub fn jsid_to_str(cx: *mut JSContext, id: HandleId) -> DOMString { } } -//http://heycam.github.io/webidl/#es-DOMString -impl FromJSValConvertible for DOMString { - type Config = StringificationBehavior; - fn from_jsval(cx: *mut JSContext, value: HandleValue, - null_behavior: StringificationBehavior) - -> Result { - if null_behavior == StringificationBehavior::Empty && - value.get().is_null() { - Ok(DOMString::new()) - } else { - let jsstr = unsafe { ToString(cx, value) }; - if jsstr.is_null() { - debug!("ToString failed"); - Err(()) - } else { - Ok(jsstring_to_str(cx, jsstr)) - } - } - } -} - //http://heycam.github.io/webidl/#es-USVString impl ToJSValConvertible for USVString { - fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { + unsafe fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { self.0.to_jsval(cx, rval); } } @@ -546,89 +150,78 @@ impl ToJSValConvertible for USVString { //http://heycam.github.io/webidl/#es-USVString impl FromJSValConvertible for USVString { type Config = (); - fn from_jsval(cx: *mut JSContext, value: HandleValue, _: ()) - -> Result { - let jsstr = unsafe { ToString(cx, value) }; + unsafe fn from_jsval(cx: *mut JSContext, value: HandleValue, _: ()) + -> Result { + let jsstr = ToString(cx, value); if jsstr.is_null() { debug!("ToString failed"); return Err(()); } - let latin1 = unsafe { JS_StringHasLatin1Chars(jsstr) }; + let latin1 = JS_StringHasLatin1Chars(jsstr); if latin1 { return Ok(USVString(jsstring_to_str(cx, jsstr).0)); } - 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))) - } + 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))) } } //http://heycam.github.io/webidl/#es-ByteString impl ToJSValConvertible for ByteString { - 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"); - } - rval.set(StringValue(&*jsstr)); + 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); + if jsstr.is_null() { + panic!("JS_NewStringCopyN failed"); } + rval.set(StringValue(&*jsstr)); } } //http://heycam.github.io/webidl/#es-ByteString impl FromJSValConvertible for ByteString { type Config = (); - fn from_jsval(cx: *mut JSContext, value: HandleValue, _option: ()) -> Result { - let string = unsafe { ToString(cx, value) }; + unsafe fn from_jsval(cx: *mut JSContext, value: HandleValue, _option: ()) -> Result { + let string = ToString(cx, value); if string.is_null() { debug!("ToString failed"); return Err(()); } - let latin1 = unsafe { JS_StringHasLatin1Chars(string) }; + let latin1 = JS_StringHasLatin1Chars(string); if latin1 { let mut length = 0; - let chars = unsafe { - JS_GetLatin1StringCharsAndLength(cx, ptr::null(), - string, &mut length) - }; + let chars = JS_GetLatin1StringCharsAndLength(cx, ptr::null(), + string, &mut length); assert!(!chars.is_null()); - let char_slice = unsafe { - slice::from_raw_parts(chars as *mut u8, length as usize) - }; - + let char_slice = slice::from_raw_parts(chars as *mut u8, length as usize); return Ok(ByteString::new(char_slice.to_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); + 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) { - throw_type_error(cx, "Invalid ByteString"); - Err(()) - } else { - Ok(ByteString::new(char_vec.iter().map(|&c| c as u8).collect())) - } + if char_vec.iter().any(|&c| c > 0xFF) { + throw_type_error(cx, "Invalid ByteString"); + Err(()) + } else { + Ok(ByteString::new(char_vec.iter().map(|&c| c as u8).collect())) } } } impl ToJSValConvertible for Reflector { - fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { + unsafe fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { let obj = self.get_jsobject().get(); assert!(!obj.is_null()); - rval.set(ObjectValue(unsafe { &*obj })); - if unsafe { !JS_WrapValue(cx, rval) } { + rval.set(ObjectValue(&*obj)); + if !JS_WrapValue(cx, rval) { panic!("JS_WrapValue failed."); } } @@ -771,75 +364,7 @@ pub fn root_from_handleobject(obj: HandleObject) -> Result, ()> } impl ToJSValConvertible for Root { - fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { + unsafe fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { self.reflector().to_jsval(cx, rval); } } - -impl<'a, T: Reflectable> ToJSValConvertible for &'a T { - fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { - self.reflector().to_jsval(cx, rval); - } -} - -impl ToJSValConvertible for Option { - fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { - match self { - &Some(ref value) => value.to_jsval(cx, rval), - &None => rval.set(NullValue()), - } - } -} - -impl ToJSValConvertible for Option> { - fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { - match self { - &Some(ref value) => (**value).to_jsval(cx, rval), - &None => rval.set(NullValue()), - } - } -} - -impl FromJSValConvertible for Option { - type Config = T::Config; - fn from_jsval(cx: *mut JSContext, value: HandleValue, option: T::Config) -> Result, ()> { - if value.get().is_null_or_undefined() { - Ok(None) - } else { - let result: Result = FromJSValConvertible::from_jsval(cx, value, option); - result.map(Some) - } - } -} - -impl ToJSValConvertible for Vec { - fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { - let js_array = RootedObject::new(cx, - unsafe { JS_NewArrayObject1(cx, self.len() as libc::size_t) }); - assert!(!js_array.handle().is_null()); - - for (index, obj) in self.iter().enumerate() { - let mut val = RootedValue::new(cx, UndefinedValue()); - obj.to_jsval(cx, val.handle_mut()); - - unsafe { - assert!(JS_DefineElement(cx, js_array.handle(), - index as u32, val.handle(), js::JSPROP_ENUMERATE, None, None)); - } - } - - unsafe { - rval.set(ObjectValue(&*js_array.handle().get())); - } - } -} - -//http://heycam.github.io/webidl/#es-object -impl ToJSValConvertible for *mut JSObject { - fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { - rval.set(ObjectOrNullValue(*self)); - unsafe { - assert!(JS_WrapValue(cx, rval)); - } - } -} diff --git a/components/script/dom/bindings/utils.rs b/components/script/dom/bindings/utils.rs index 0ce5aeddf5ed..774738a3030b 100644 --- a/components/script/dom/bindings/utils.rs +++ b/components/script/dom/bindings/utils.rs @@ -6,7 +6,7 @@ use dom::bindings::codegen::PrototypeList; use dom::bindings::codegen::PrototypeList::MAX_PROTO_CHAIN_LENGTH; -use dom::bindings::conversions::{DOM_OBJECT_SLOT, is_dom_class, jsstring_to_str}; +use dom::bindings::conversions::{DOM_OBJECT_SLOT, is_dom_class}; use dom::bindings::conversions::{private_from_proto_check, root_from_handleobject}; use dom::bindings::error::throw_invalid_this; use dom::bindings::inheritance::TopTypeId; @@ -50,6 +50,7 @@ use std::default::Default; use std::ffi::CString; use std::ptr; use util::mem::HeapSizeOf; +use util::str::jsstring_to_str; /// Proxy handler for a WindowProxy. #[allow(raw_pointer_derive)] @@ -441,11 +442,11 @@ pub fn get_array_index_from_id(_cx: *mut JSContext, id: HandleId) -> Option /// Find the index of a string given by `v` in `values`. /// 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: HandleValue, - values: &[&'static str]) - -> Result, ()> { - let jsstr = unsafe { ToString(cx, v) }; +pub unsafe fn find_enum_string_index(cx: *mut JSContext, + v: HandleValue, + values: &[&'static str]) + -> Result, ()> { + let jsstr = ToString(cx, v); if jsstr.is_null() { return Err(()); } diff --git a/components/script/dom/htmliframeelement.rs b/components/script/dom/htmliframeelement.rs index 6bc48da1c4fd..1665c60d58f9 100644 --- a/components/script/dom/htmliframeelement.rs +++ b/components/script/dom/htmliframeelement.rs @@ -130,6 +130,7 @@ impl HTMLIFrameElement { self.navigate_child_browsing_context(url); } + #[allow(unsafe_code)] pub fn dispatch_mozbrowser_event(&self, event: MozBrowserEvent) { // TODO(gw): Support mozbrowser event types that have detail which is not a string. // See https://developer.mozilla.org/en-US/docs/Web/API/Using_the_Browser_API @@ -138,16 +139,18 @@ impl HTMLIFrameElement { if self.Mozbrowser() { let window = window_from_node(self); - let cx = window.get_cx(); - let _ar = JSAutoRequest::new(cx); - let _ac = JSAutoCompartment::new(cx, window.reflector().get_jsobject().get()); - let mut detail = RootedValue::new(cx, UndefinedValue()); - event.detail().to_jsval(cx, detail.handle_mut()); - let custom_event = CustomEvent::new(GlobalRef::Window(window.r()), - DOMString(event.name().to_owned()), - true, - true, - detail.handle()); + let custom_event = unsafe { + let cx = window.get_cx(); + let _ar = JSAutoRequest::new(cx); + let _ac = JSAutoCompartment::new(cx, window.reflector().get_jsobject().get()); + let mut detail = RootedValue::new(cx, UndefinedValue()); + event.detail().to_jsval(cx, detail.handle_mut()); + CustomEvent::new(GlobalRef::Window(window.r()), + DOMString(event.name().to_owned()), + true, + true, + detail.handle()) + }; custom_event.upcast::().fire(self.upcast()); } } diff --git a/components/script/dom/webglrenderingcontext.rs b/components/script/dom/webglrenderingcontext.rs index 4855bcc36de3..c0a00adf1029 100644 --- a/components/script/dom/webglrenderingcontext.rs +++ b/components/script/dom/webglrenderingcontext.rs @@ -189,19 +189,22 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { receiver.recv().unwrap() } + #[allow(unsafe_code)] // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3 fn GetParameter(&self, cx: *mut JSContext, parameter: u32) -> JSVal { // TODO(ecoal95): Implement the missing parameters from the spec - let mut rval = RootedValue::new(cx, UndefinedValue()); - match parameter { - constants::VERSION => - "WebGL 1.0".to_jsval(cx, rval.handle_mut()), - constants::RENDERER | - constants::VENDOR => - "Mozilla/Servo".to_jsval(cx, rval.handle_mut()), - _ => rval.ptr = NullValue(), - } - rval.ptr + unsafe { + let mut rval = RootedValue::new(cx, UndefinedValue()); + match parameter { + constants::VERSION => + "WebGL 1.0".to_jsval(cx, rval.handle_mut()), + constants::RENDERER | + constants::VENDOR => + "Mozilla/Servo".to_jsval(cx, rval.handle_mut()), + _ => rval.ptr = NullValue(), + } + rval.ptr + } } // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3 diff --git a/components/script/dom/websocket.rs b/components/script/dom/websocket.rs index ea9c0e243aa7..2f7994c373e5 100644 --- a/components/script/dom/websocket.rs +++ b/components/script/dom/websocket.rs @@ -570,32 +570,32 @@ impl Runnable for MessageReceivedTask { // Step 2-5. let global = ws.global.root(); - let cx = global.r().get_cx(); - let _ar = JSAutoRequest::new(cx); - let _ac = JSAutoCompartment::new(cx, ws.reflector().get_jsobject().get()); - let mut message = RootedValue::new(cx, UndefinedValue()); - match self.message { - MessageData::Text(text) => text.to_jsval(cx, message.handle_mut()), - MessageData::Binary(data) => { - match ws.binary_type.get() { - BinaryType::Blob => { - let blob = Blob::new(global.r(), Some(data), ""); - blob.to_jsval(cx, message.handle_mut()); - } - BinaryType::Arraybuffer => { - unsafe { + // global.get_cx() returns a valid `JSContext` pointer, so this is safe. + unsafe { + let cx = global.r().get_cx(); + let _ar = JSAutoRequest::new(cx); + let _ac = JSAutoCompartment::new(cx, ws.reflector().get_jsobject().get()); + let mut message = RootedValue::new(cx, UndefinedValue()); + match self.message { + MessageData::Text(text) => text.to_jsval(cx, message.handle_mut()), + MessageData::Binary(data) => { + match ws.binary_type.get() { + BinaryType::Blob => { + let blob = Blob::new(global.r(), Some(data), ""); + blob.to_jsval(cx, message.handle_mut()); + } + BinaryType::Arraybuffer => { let len = data.len() as uint32_t; let buf = JS_NewArrayBuffer(cx, len); let buf_data: *mut uint8_t = JS_GetArrayBufferData(buf, ptr::null()); ptr::copy_nonoverlapping(data.as_ptr(), buf_data, len as usize); buf.to_jsval(cx, message.handle_mut()); } - } - } - }, + } + }, + } + MessageEvent::dispatch_jsval(ws.upcast(), global.r(), message.handle()); } - - MessageEvent::dispatch_jsval(ws.upcast(), global.r(), message.handle()); } } diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index c7f0af1b234e..09f14f125105 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -574,8 +574,9 @@ impl WindowMethods for Window { breakpoint(); } + #[allow(unsafe_code)] fn WebdriverCallback(&self, cx: *mut JSContext, val: HandleValue) { - let rv = jsval_to_webdriver(cx, val); + let rv = unsafe { jsval_to_webdriver(cx, val) }; let opt_chan = self.webdriver_script_chan.borrow_mut().take(); if let Some(chan) = opt_chan { chan.send(rv).unwrap(); diff --git a/components/script/dom/xmlhttprequest.rs b/components/script/dom/xmlhttprequest.rs index da5aab7710f7..adea59382b3d 100644 --- a/components/script/dom/xmlhttprequest.rs +++ b/components/script/dom/xmlhttprequest.rs @@ -684,23 +684,23 @@ impl XMLHttpRequestMethods for XMLHttpRequest { #[allow(unsafe_code)] // https://xhr.spec.whatwg.org/#the-response-attribute fn Response(&self, cx: *mut JSContext) -> JSVal { - let mut rval = RootedValue::new(cx, UndefinedValue()); - match self.response_type.get() { - _empty | Text => { - let ready_state = self.ready_state.get(); - if ready_state == XMLHttpRequestState::Done || ready_state == XMLHttpRequestState::Loading { - self.text_response().to_jsval(cx, rval.handle_mut()); - } else { - "".to_jsval(cx, rval.handle_mut()); - } - }, - _ if self.ready_state.get() != XMLHttpRequestState::Done => { - return NullValue() - }, - Json => { - let decoded = UTF_8.decode(&self.response.borrow(), DecoderTrap::Replace).unwrap().to_owned(); - let decoded: Vec = decoded.utf16_units().collect(); - unsafe { + unsafe { + let mut rval = RootedValue::new(cx, UndefinedValue()); + match self.response_type.get() { + _empty | Text => { + let ready_state = self.ready_state.get(); + if ready_state == XMLHttpRequestState::Done || ready_state == XMLHttpRequestState::Loading { + self.text_response().to_jsval(cx, rval.handle_mut()); + } else { + "".to_jsval(cx, rval.handle_mut()); + } + }, + _ if self.ready_state.get() != XMLHttpRequestState::Done => { + return NullValue() + }, + Json => { + let decoded = UTF_8.decode(&self.response.borrow(), DecoderTrap::Replace).unwrap().to_owned(); + let decoded: Vec = decoded.utf16_units().collect(); if !JS_ParseJSON(cx, decoded.as_ptr(), decoded.len() as u32, @@ -709,13 +709,13 @@ impl XMLHttpRequestMethods for XMLHttpRequest { return NullValue(); } } + _ => { + // XXXManishearth handle other response types + self.response.borrow().to_jsval(cx, rval.handle_mut()); + } } - _ => { - // XXXManishearth handle other response types - self.response.borrow().to_jsval(cx, rval.handle_mut()); - } + rval.ptr } - rval.ptr } // https://xhr.spec.whatwg.org/#the-responsetext-attribute diff --git a/components/script/lib.rs b/components/script/lib.rs index e4481fb7d49e..3ef9edff9ff6 100644 --- a/components/script/lib.rs +++ b/components/script/lib.rs @@ -12,7 +12,6 @@ #![feature(core_intrinsics)] #![feature(custom_attribute)] #![feature(custom_derive)] -#![feature(decode_utf16)] #![feature(drain)] #![feature(fnbox)] #![feature(hashmap_hasher)] diff --git a/components/script/script_task.rs b/components/script/script_task.rs index 99312a61bd69..e97a8141dbe5 100644 --- a/components/script/script_task.rs +++ b/components/script/script_task.rs @@ -1683,11 +1683,13 @@ impl ScriptTask { let script_source = String::from_utf8_lossy(&script_source_bytes); // Script source is ready to be evaluated (11.) - let mut jsval = RootedValue::new(self.get_cx(), UndefinedValue()); - window.evaluate_js_on_global_with_result(&script_source, jsval.handle_mut()); - let strval = DOMString::from_jsval(self.get_cx(), jsval.handle(), - StringificationBehavior::Empty); - strval.unwrap_or(DOMString::new()) + unsafe { + let mut jsval = RootedValue::new(self.get_cx(), UndefinedValue()); + window.evaluate_js_on_global_with_result(&script_source, jsval.handle_mut()); + let strval = DOMString::from_jsval(self.get_cx(), jsval.handle(), + StringificationBehavior::Empty); + strval.unwrap_or(DOMString::new()) + } } else { DOMString::new() }; diff --git a/components/script/webdriver_handlers.rs b/components/script/webdriver_handlers.rs index 3fcd6ef1d0cd..9f095fab3099 100644 --- a/components/script/webdriver_handlers.rs +++ b/components/script/webdriver_handlers.rs @@ -40,7 +40,8 @@ fn find_node_by_unique_id(page: &Rc, pipeline: PipelineId, node_id: String None } -pub fn jsval_to_webdriver(cx: *mut JSContext, val: HandleValue) -> WebDriverJSResult { +#[allow(unsafe_code)] +pub unsafe fn jsval_to_webdriver(cx: *mut JSContext, val: HandleValue) -> WebDriverJSResult { if val.get().is_undefined() { Ok(WebDriverJSValue::Undefined) } else if val.get().is_boolean() { @@ -58,17 +59,20 @@ pub fn jsval_to_webdriver(cx: *mut JSContext, val: HandleValue) -> WebDriverJSRe } } +#[allow(unsafe_code)] pub fn handle_execute_script(page: &Rc, pipeline: PipelineId, eval: String, reply: IpcSender) { let page = get_page(&*page, pipeline); let window = page.window(); - let cx = window.get_cx(); - let mut rval = RootedValue::new(cx, UndefinedValue()); - window.evaluate_js_on_global_with_result(&eval, rval.handle_mut()); - - reply.send(jsval_to_webdriver(cx, rval.handle())).unwrap(); + let result = unsafe { + let cx = window.get_cx(); + let mut rval = RootedValue::new(cx, UndefinedValue()); + window.evaluate_js_on_global_with_result(&eval, rval.handle_mut()); + jsval_to_webdriver(cx, rval.handle()) + }; + reply.send(result).unwrap(); } pub fn handle_execute_async_script(page: &Rc, diff --git a/components/util/lib.rs b/components/util/lib.rs index 850998408f19..abcb686723a6 100644 --- a/components/util/lib.rs +++ b/components/util/lib.rs @@ -6,6 +6,7 @@ #![feature(box_syntax)] #![feature(core_intrinsics)] #![feature(custom_derive)] +#![feature(decode_utf16)] #![feature(fnbox)] #![feature(hashmap_hasher)] #![feature(heap_api)] diff --git a/components/util/str.rs b/components/util/str.rs index 80e5fb04d6c7..afbced05ac46 100644 --- a/components/util/str.rs +++ b/components/util/str.rs @@ -4,15 +4,23 @@ use app_units::Au; use cssparser::{self, Color, RGBA}; +use js::conversions::{FromJSValConvertible, ToJSValConvertible, latin1_to_string}; +use js::jsapi::{JSContext, JSString, HandleValue, MutableHandleValue}; +use js::jsapi::{JS_GetTwoByteStringCharsAndLength, JS_StringHasLatin1Chars}; +use js::rust::ToString; use libc::c_char; use num_lib::ToPrimitive; +use opts; use std::ascii::AsciiExt; use std::borrow::ToOwned; +use std::char; use std::convert::AsRef; use std::ffi::CStr; use std::fmt; use std::iter::{Filter, Peekable}; use std::ops::{Deref, DerefMut}; +use std::ptr; +use std::slice; use std::str::{CharIndices, FromStr, Split, from_utf8}; #[derive(Clone, PartialOrd, Ord, PartialEq, Eq, Deserialize, Serialize, Hash, Debug)] @@ -71,6 +79,82 @@ impl Into> for DOMString { } } +// https://heycam.github.io/webidl/#es-DOMString +impl ToJSValConvertible for DOMString { + unsafe fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { + (**self).to_jsval(cx, rval); + } +} + +/// Behavior for stringification of `JSVal`s. +#[derive(PartialEq)] +pub enum StringificationBehavior { + /// Convert `null` to the string `"null"`. + Default, + /// Convert `null` to the empty string. + Empty, +} + +/// 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); + DOMString(if latin1 { + latin1_to_string(cx, s) + } else { + let mut length = 0; + let chars = JS_GetTwoByteStringCharsAndLength(cx, ptr::null(), s, &mut length); + assert!(!chars.is_null()); + let potentially_ill_formed_utf16 = slice::from_raw_parts(chars, length as usize); + let mut s = String::with_capacity(length as usize); + for item in char::decode_utf16(potentially_ill_formed_utf16.iter().cloned()) { + match item { + Ok(c) => s.push(c), + Err(_) => { + // FIXME: Add more info like document URL in the message? + macro_rules! message { + () => { + "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.")); + } + } + } + } + s + }) +} + +// 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 { + if null_behavior == StringificationBehavior::Empty && + value.get().is_null() { + Ok(DOMString::new()) + } else { + let jsstr = ToString(cx, value); + if jsstr.is_null() { + debug!("ToString failed"); + Err(()) + } else { + Ok(jsstring_to_str(cx, jsstr)) + } + } + } +} + pub type StaticCharVec = &'static [char]; pub type StaticStringVec = &'static [&'static str];