mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-22 17:55:50 +00:00
servo: Merge #6582 - Add Clamp and EnforceRange support for webidl arguments (from servo:clamp-enforce-range); r=jdm
Source-Repo: https://github.com/servo/servo Source-Revision: 36d732a60a39140806b4113d58a6b00e6b1931da
This commit is contained in:
parent
69f6c7e1ca
commit
062b7d8932
@ -832,7 +832,9 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
|
||||
if treatNullAs not in treatAs:
|
||||
raise TypeError("We don't support [TreatNullAs=%s]" % treatNullAs)
|
||||
if type.nullable():
|
||||
nullBehavior = "()"
|
||||
# Note: the actual behavior passed here doesn't matter for nullable
|
||||
# strings.
|
||||
nullBehavior = "StringificationBehavior::Default"
|
||||
else:
|
||||
nullBehavior = treatAs[treatNullAs]
|
||||
|
||||
@ -1046,7 +1048,16 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
|
||||
if not type.isPrimitive():
|
||||
raise TypeError("Need conversion for argument type '%s'" % str(type))
|
||||
|
||||
assert not isEnforceRange and not isClamp
|
||||
if type.isInteger():
|
||||
if isEnforceRange:
|
||||
conversionBehavior = "ConversionBehavior::EnforceRange"
|
||||
elif isClamp:
|
||||
conversionBehavior = "ConversionBehavior::Clamp"
|
||||
else:
|
||||
conversionBehavior = "ConversionBehavior::Default"
|
||||
else:
|
||||
assert not isEnforceRange and not isClamp
|
||||
conversionBehavior = "()"
|
||||
|
||||
if failureCode is None:
|
||||
failureCode = 'return 0'
|
||||
@ -1055,12 +1066,11 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
|
||||
if type.nullable():
|
||||
declType = CGWrapper(declType, pre="Option<", post=">")
|
||||
|
||||
# XXXjdm support conversionBehavior here
|
||||
template = (
|
||||
"match FromJSValConvertible::from_jsval(cx, ${val}, ()) {\n"
|
||||
"match FromJSValConvertible::from_jsval(cx, ${val}, %s) {\n"
|
||||
" Ok(v) => v,\n"
|
||||
" Err(_) => { %s }\n"
|
||||
"}" % exceptionCode)
|
||||
"}" % (conversionBehavior, exceptionCode))
|
||||
|
||||
if defaultValue is not None:
|
||||
if isinstance(defaultValue, IDLNullValue):
|
||||
@ -2002,6 +2012,7 @@ def UnionTypes(descriptors, dictionaries, callbacks, config):
|
||||
'dom::bindings::codegen::PrototypeList',
|
||||
'dom::bindings::conversions::FromJSValConvertible',
|
||||
'dom::bindings::conversions::ToJSValConvertible',
|
||||
'dom::bindings::conversions::ConversionBehavior',
|
||||
'dom::bindings::conversions::native_from_handlevalue',
|
||||
'dom::bindings::conversions::StringificationBehavior',
|
||||
'dom::bindings::error::throw_not_in_union',
|
||||
@ -5209,7 +5220,7 @@ class CGBindingRoot(CGThing):
|
||||
'dom::bindings::callback::{CallbackContainer,CallbackInterface,CallbackFunction}',
|
||||
'dom::bindings::callback::{CallSetup,ExceptionHandling}',
|
||||
'dom::bindings::callback::wrap_call_this_object',
|
||||
'dom::bindings::conversions::{FromJSValConvertible, ToJSValConvertible}',
|
||||
'dom::bindings::conversions::{FromJSValConvertible, ToJSValConvertible, ConversionBehavior}',
|
||||
'dom::bindings::conversions::{native_from_reflector, native_from_handlevalue, native_from_handleobject}',
|
||||
'dom::bindings::conversions::DOM_OBJECT_SLOT',
|
||||
'dom::bindings::conversions::IDLInterface',
|
||||
|
@ -57,13 +57,54 @@ use js::jsval::{StringValue, ObjectValue, ObjectOrNullValue};
|
||||
|
||||
use libc;
|
||||
use num::Float;
|
||||
use num::traits::{Bounded, Zero};
|
||||
use std::borrow::ToOwned;
|
||||
use std::default;
|
||||
use std::slice;
|
||||
use std::ptr;
|
||||
use std::rc::Rc;
|
||||
use core::nonzero::NonZero;
|
||||
|
||||
trait As<O>: Copy {
|
||||
fn cast(self) -> O;
|
||||
}
|
||||
|
||||
macro_rules! impl_as {
|
||||
($I:ty, $O:ty) => (
|
||||
impl As<$O> for $I {
|
||||
fn cast(self) -> $O {
|
||||
self as $O
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
impl_as!(f64, u8);
|
||||
impl_as!(f64, u16);
|
||||
impl_as!(f64, u32);
|
||||
impl_as!(f64, u64);
|
||||
impl_as!(f64, i8);
|
||||
impl_as!(f64, i16);
|
||||
impl_as!(f64, i32);
|
||||
impl_as!(f64, i64);
|
||||
|
||||
impl_as!(u8, f64);
|
||||
impl_as!(u16, f64);
|
||||
impl_as!(u32, f64);
|
||||
impl_as!(u64, f64);
|
||||
impl_as!(i8, f64);
|
||||
impl_as!(i16, f64);
|
||||
impl_as!(i32, f64);
|
||||
impl_as!(i64, f64);
|
||||
|
||||
impl_as!(i32, i8);
|
||||
impl_as!(i32, u8);
|
||||
impl_as!(i32, i16);
|
||||
impl_as!(u16, u16);
|
||||
impl_as!(i32, i32);
|
||||
impl_as!(u32, u32);
|
||||
impl_as!(i64, i64);
|
||||
impl_as!(u64, u64);
|
||||
|
||||
/// A trait to retrieve the constants necessary to check if a `JSObject`
|
||||
/// implements a given interface.
|
||||
pub trait IDLInterface {
|
||||
@ -91,6 +132,54 @@ pub trait FromJSValConvertible {
|
||||
fn from_jsval(cx: *mut JSContext, val: HandleValue, option: Self::Config) -> Result<Self, ()>;
|
||||
}
|
||||
|
||||
/// 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<D>(cx: *mut JSContext, d: f64) -> Result<D, ()>
|
||||
where D: Bounded + As<f64>,
|
||||
f64: As<D>
|
||||
{
|
||||
if d.is_infinite() {
|
||||
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 {
|
||||
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>(d: f64) -> D
|
||||
where D: Bounded + As<f64> + Zero,
|
||||
f64: As<D>
|
||||
{
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
impl ToJSValConvertible for () {
|
||||
fn to_jsval(&self, _cx: *mut JSContext, rval: MutableHandleValue) {
|
||||
@ -116,6 +205,22 @@ impl ToJSValConvertible for HandleValue {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn convert_int_from_jsval<T, M>(cx: *mut JSContext, value: HandleValue,
|
||||
option: ConversionBehavior,
|
||||
convert_fn: fn(*mut JSContext, HandleValue) -> Result<M, ()>)
|
||||
-> Result<T, ()>
|
||||
where T: Bounded + Zero + As<f64>,
|
||||
M: Zero + As<T>,
|
||||
f64: As<T>
|
||||
{
|
||||
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)))),
|
||||
}
|
||||
}
|
||||
|
||||
impl ToJSValConvertible for bool {
|
||||
fn to_jsval(&self, _cx: *mut JSContext, rval: MutableHandleValue) {
|
||||
rval.set(BooleanValue(*self));
|
||||
@ -136,10 +241,9 @@ impl ToJSValConvertible for i8 {
|
||||
}
|
||||
|
||||
impl FromJSValConvertible for i8 {
|
||||
type Config = ();
|
||||
fn from_jsval(cx: *mut JSContext, val: HandleValue, _option: ()) -> Result<i8, ()> {
|
||||
let result = ToInt32(cx, val);
|
||||
result.map(|v| v as i8)
|
||||
type Config = ConversionBehavior;
|
||||
fn from_jsval(cx: *mut JSContext, val: HandleValue, option: ConversionBehavior) -> Result<i8, ()> {
|
||||
convert_int_from_jsval(cx, val, option, ToInt32)
|
||||
}
|
||||
}
|
||||
|
||||
@ -150,10 +254,9 @@ impl ToJSValConvertible for u8 {
|
||||
}
|
||||
|
||||
impl FromJSValConvertible for u8 {
|
||||
type Config = ();
|
||||
fn from_jsval(cx: *mut JSContext, val: HandleValue, _option: ()) -> Result<u8, ()> {
|
||||
let result = ToInt32(cx, val);
|
||||
result.map(|v| v as u8)
|
||||
type Config = ConversionBehavior;
|
||||
fn from_jsval(cx: *mut JSContext, val: HandleValue, option: ConversionBehavior) -> Result<u8, ()> {
|
||||
convert_int_from_jsval(cx, val, option, ToInt32)
|
||||
}
|
||||
}
|
||||
|
||||
@ -164,10 +267,9 @@ impl ToJSValConvertible for i16 {
|
||||
}
|
||||
|
||||
impl FromJSValConvertible for i16 {
|
||||
type Config = ();
|
||||
fn from_jsval(cx: *mut JSContext, val: HandleValue, _option: ()) -> Result<i16, ()> {
|
||||
let result = ToInt32(cx, val);
|
||||
result.map(|v| v as i16)
|
||||
type Config = ConversionBehavior;
|
||||
fn from_jsval(cx: *mut JSContext, val: HandleValue, option: ConversionBehavior) -> Result<i16, ()> {
|
||||
convert_int_from_jsval(cx, val, option, ToInt32)
|
||||
}
|
||||
}
|
||||
|
||||
@ -178,9 +280,9 @@ impl ToJSValConvertible for u16 {
|
||||
}
|
||||
|
||||
impl FromJSValConvertible for u16 {
|
||||
type Config = ();
|
||||
fn from_jsval(cx: *mut JSContext, val: HandleValue, _option: ()) -> Result<u16, ()> {
|
||||
ToUint16(cx, val)
|
||||
type Config = ConversionBehavior;
|
||||
fn from_jsval(cx: *mut JSContext, val: HandleValue, option: ConversionBehavior) -> Result<u16, ()> {
|
||||
convert_int_from_jsval(cx, val, option, ToUint16)
|
||||
}
|
||||
}
|
||||
|
||||
@ -191,9 +293,9 @@ impl ToJSValConvertible for i32 {
|
||||
}
|
||||
|
||||
impl FromJSValConvertible for i32 {
|
||||
type Config = ();
|
||||
fn from_jsval(cx: *mut JSContext, val: HandleValue, _option: ()) -> Result<i32, ()> {
|
||||
ToInt32(cx, val)
|
||||
type Config = ConversionBehavior;
|
||||
fn from_jsval(cx: *mut JSContext, val: HandleValue, option: ConversionBehavior) -> Result<i32, ()> {
|
||||
convert_int_from_jsval(cx, val, option, ToInt32)
|
||||
}
|
||||
}
|
||||
|
||||
@ -204,9 +306,9 @@ impl ToJSValConvertible for u32 {
|
||||
}
|
||||
|
||||
impl FromJSValConvertible for u32 {
|
||||
type Config = ();
|
||||
fn from_jsval(cx: *mut JSContext, val: HandleValue, _option: ()) -> Result<u32, ()> {
|
||||
ToUint32(cx, val)
|
||||
type Config = ConversionBehavior;
|
||||
fn from_jsval(cx: *mut JSContext, val: HandleValue, option: ConversionBehavior) -> Result<u32, ()> {
|
||||
convert_int_from_jsval(cx, val, option, ToUint32)
|
||||
}
|
||||
}
|
||||
|
||||
@ -219,9 +321,9 @@ impl ToJSValConvertible for i64 {
|
||||
}
|
||||
|
||||
impl FromJSValConvertible for i64 {
|
||||
type Config = ();
|
||||
fn from_jsval(cx: *mut JSContext, val: HandleValue, _option: ()) -> Result<i64, ()> {
|
||||
ToInt64(cx, val)
|
||||
type Config = ConversionBehavior;
|
||||
fn from_jsval(cx: *mut JSContext, val: HandleValue, option: ConversionBehavior) -> Result<i64, ()> {
|
||||
convert_int_from_jsval(cx, val, option, ToInt64)
|
||||
}
|
||||
}
|
||||
|
||||
@ -234,9 +336,9 @@ impl ToJSValConvertible for u64 {
|
||||
}
|
||||
|
||||
impl FromJSValConvertible for u64 {
|
||||
type Config = ();
|
||||
fn from_jsval(cx: *mut JSContext, val: HandleValue, _option: ()) -> Result<u64, ()> {
|
||||
ToUint64(cx, val)
|
||||
type Config = ConversionBehavior;
|
||||
fn from_jsval(cx: *mut JSContext, val: HandleValue, option: ConversionBehavior) -> Result<u64, ()> {
|
||||
convert_int_from_jsval(cx, val, option, ToUint64)
|
||||
}
|
||||
}
|
||||
|
||||
@ -323,12 +425,6 @@ pub enum StringificationBehavior {
|
||||
Empty,
|
||||
}
|
||||
|
||||
impl default::Default for StringificationBehavior {
|
||||
fn default() -> StringificationBehavior {
|
||||
StringificationBehavior::Default
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
@ -666,13 +762,12 @@ impl<T: ToJSValConvertible> ToJSValConvertible for Option<Rc<T>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<X: default::Default, T: FromJSValConvertible<Config=X>> FromJSValConvertible for Option<T> {
|
||||
type Config = ();
|
||||
fn from_jsval(cx: *mut JSContext, value: HandleValue, _: ()) -> Result<Option<T>, ()> {
|
||||
impl<T: FromJSValConvertible> FromJSValConvertible for Option<T> {
|
||||
type Config = T::Config;
|
||||
fn from_jsval(cx: *mut JSContext, value: HandleValue, option: T::Config) -> Result<Option<T>, ()> {
|
||||
if value.get().is_null_or_undefined() {
|
||||
Ok(None)
|
||||
} else {
|
||||
let option: X = default::Default::default();
|
||||
let result: Result<T, ()> = FromJSValConvertible::from_jsval(cx, value, option);
|
||||
result.map(Some)
|
||||
}
|
||||
|
@ -16,12 +16,8 @@ interface Blob {
|
||||
|
||||
//slice Blob into byte-ranged chunks
|
||||
|
||||
//TODO: implement slice with [Clamp]
|
||||
//Blob slice([Clamp] optional long long start,
|
||||
// [Clamp] optional long long end,
|
||||
// optional DOMString contentType);
|
||||
Blob slice(optional long long start,
|
||||
optional long long end,
|
||||
Blob slice([Clamp] optional long long start,
|
||||
[Clamp] optional long long end,
|
||||
optional DOMString contentType);
|
||||
//void close();
|
||||
|
||||
|
@ -21,8 +21,7 @@ interface WebSocket : EventTarget {
|
||||
attribute EventHandler onclose;
|
||||
//readonly attribute DOMString extensions;
|
||||
//readonly attribute DOMString protocol;
|
||||
//[Throws] void close([Clamp] optional unsigned short code, optional USVString reason); //Clamp doesn't work
|
||||
[Throws] void close(optional unsigned short code, optional USVString reason); //No clamp version - works
|
||||
[Throws] void close([Clamp] optional unsigned short code, optional USVString reason);
|
||||
|
||||
//messaging
|
||||
//attribute EventHandler onmessage;
|
||||
|
Loading…
Reference in New Issue
Block a user