diff options
Diffstat (limited to 'components/script/dom/bindings')
-rw-r--r-- | components/script/dom/bindings/codegen/CodegenRust.py | 37 | ||||
-rw-r--r-- | components/script/dom/bindings/mod.rs | 2 | ||||
-rw-r--r-- | components/script/dom/bindings/mozmap.rs | 135 | ||||
-rw-r--r-- | components/script/dom/bindings/record.rs | 199 | ||||
-rw-r--r-- | components/script/dom/bindings/str.rs | 2 |
5 files changed, 231 insertions, 144 deletions
diff --git a/components/script/dom/bindings/codegen/CodegenRust.py b/components/script/dom/bindings/codegen/CodegenRust.py index 103450f78f3..01b7fc698ba 100644 --- a/components/script/dom/bindings/codegen/CodegenRust.py +++ b/components/script/dom/bindings/codegen/CodegenRust.py @@ -77,14 +77,13 @@ def innerContainerType(type): def wrapInNativeContainerType(type, inner): if type.isSequence(): - containerType = "Vec" + return CGWrapper(inner, pre="Vec<", post=">") elif type.isRecord(): - containerType = "MozMap" + key = type.inner.keyType if type.nullable() else type.keyType + return CGRecord(key, inner) else: raise TypeError("Unexpected container type %s", type) - return CGWrapper(inner, pre=containerType + "<", post=">") - builtinNames = { IDLType.Tags.bool: 'bool', @@ -1905,6 +1904,30 @@ class CGWrapper(CGThing): return self.pre + defn + self.post +class CGRecord(CGThing): + """ + CGThing that wraps value CGThing in record with key type equal to keyType parameter + """ + def __init__(self, keyType, value): + CGThing.__init__(self) + assert keyType.isString() + self.keyType = keyType + self.value = value + + def define(self): + if self.keyType.isByteString(): + keyDef = "ByteString" + elif self.keyType.isDOMString(): + keyDef = "DOMString" + elif self.keyType.isUSVString(): + keyDef = "USVString" + else: + assert False + + defn = keyDef + ", " + self.value.define() + return "Record<" + defn + ">" + + class CGImports(CGWrapper): """ Generates the appropriate import/use statements. @@ -2024,7 +2047,7 @@ class CGImports(CGWrapper): extras += [descriptor.path, descriptor.bindingPath] parentName = descriptor.getParentName() elif t.isType() and t.isRecord(): - extras += ['crate::dom::bindings::mozmap::MozMap'] + extras += ['crate::dom::bindings::record::Record'] elif isinstance(t, IDLPromiseType): extras += ['crate::dom::promise::Promise'] else: @@ -2373,7 +2396,7 @@ def UnionTypes(descriptors, dictionaries, callbacks, typedefs, config): 'crate::dom::bindings::conversions::StringificationBehavior', 'crate::dom::bindings::conversions::root_from_handlevalue', 'std::ptr::NonNull', - 'crate::dom::bindings::mozmap::MozMap', + 'crate::dom::bindings::record::Record', 'crate::dom::bindings::num::Finite', 'crate::dom::bindings::root::DomRoot', 'crate::dom::bindings::str::ByteString', @@ -6054,7 +6077,7 @@ def generate_imports(config, cgthings, descriptors, callbacks=None, dictionaries 'crate::dom::bindings::proxyhandler::ensure_expando_object', 'crate::dom::bindings::proxyhandler::fill_property_descriptor', 'crate::dom::bindings::proxyhandler::get_expando_object', - 'crate::dom::bindings::mozmap::MozMap', + 'crate::dom::bindings::record::Record', 'std::ptr::NonNull', 'crate::dom::bindings::num::Finite', 'crate::dom::bindings::str::ByteString', diff --git a/components/script/dom/bindings/mod.rs b/components/script/dom/bindings/mod.rs index c6759edb360..48ee7674c92 100644 --- a/components/script/dom/bindings/mod.rs +++ b/components/script/dom/bindings/mod.rs @@ -143,10 +143,10 @@ pub mod htmlconstructor; pub mod inheritance; pub mod interface; pub mod iterable; -pub mod mozmap; pub mod namespace; pub mod num; pub mod proxyhandler; +pub mod record; pub mod refcounted; pub mod reflector; pub mod root; diff --git a/components/script/dom/bindings/mozmap.rs b/components/script/dom/bindings/mozmap.rs deleted file mode 100644 index f5961e5bdda..00000000000 --- a/components/script/dom/bindings/mozmap.rs +++ /dev/null @@ -1,135 +0,0 @@ -/* 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 https://mozilla.org/MPL/2.0/. */ - -//! The `MozMap` (open-ended dictionary) type. - -use crate::dom::bindings::conversions::jsid_to_string; -use crate::dom::bindings::error::report_pending_exception; -use crate::dom::bindings::str::DOMString; -use indexmap::IndexMap; -use js::conversions::{ConversionResult, FromJSValConvertible, ToJSValConvertible}; -use js::jsapi::JSContext; -use js::jsapi::JS_NewPlainObject; -use js::jsapi::JSITER_HIDDEN; -use js::jsapi::JSITER_OWNONLY; -use js::jsapi::JSITER_SYMBOLS; -use js::jsapi::JSPROP_ENUMERATE; -use js::jsval::ObjectValue; -use js::jsval::UndefinedValue; -use js::rust::wrappers::GetPropertyKeys; -use js::rust::wrappers::JS_DefineUCProperty2; -use js::rust::wrappers::JS_GetPropertyById; -use js::rust::HandleValue; -use js::rust::IdVector; -use js::rust::MutableHandleValue; -use std::ops::Deref; - -/// The `MozMap` (open-ended dictionary) type. -#[derive(Clone, JSTraceable)] -pub struct MozMap<T> { - map: IndexMap<DOMString, T>, -} - -impl<T> MozMap<T> { - /// Create an empty `MozMap`. - pub fn new() -> Self { - MozMap { - map: IndexMap::new(), - } - } -} - -impl<T> Deref for MozMap<T> { - type Target = IndexMap<DOMString, T>; - - fn deref(&self) -> &IndexMap<DOMString, T> { - &self.map - } -} - -impl<T, C> FromJSValConvertible for MozMap<T> -where - T: FromJSValConvertible<Config = C>, - C: Clone, -{ - type Config = C; - unsafe fn from_jsval( - cx: *mut JSContext, - value: HandleValue, - config: C, - ) -> Result<ConversionResult<Self>, ()> { - if !value.is_object() { - return Ok(ConversionResult::Failure( - "MozMap value was not an object".into(), - )); - } - - rooted!(in(cx) let object = value.to_object()); - let ids = IdVector::new(cx); - if !GetPropertyKeys( - cx, - object.handle(), - JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, - ids.get(), - ) { - // TODO: can GetPropertyKeys fail? - // (it does so if the object has duplicate keys) - // https://github.com/servo/servo/issues/21462 - report_pending_exception(cx, false); - return Ok(ConversionResult::Failure( - "Getting MozMap value property keys failed".into(), - )); - } - - let mut map = IndexMap::new(); - for id in &*ids { - rooted!(in(cx) let id = *id); - - rooted!(in(cx) let mut property = UndefinedValue()); - if !JS_GetPropertyById(cx, object.handle(), id.handle(), property.handle_mut()) { - return Err(()); - } - - let property = match T::from_jsval(cx, property.handle(), config.clone())? { - ConversionResult::Success(property) => property, - ConversionResult::Failure(message) => { - return Ok(ConversionResult::Failure(message)) - }, - }; - - // TODO: Is this guaranteed to succeed? - // https://github.com/servo/servo/issues/21463 - if let Some(key) = jsid_to_string(cx, id.handle()) { - map.insert(key, property); - } - } - - Ok(ConversionResult::Success(MozMap { map: map })) - } -} - -impl<T: ToJSValConvertible> ToJSValConvertible for MozMap<T> { - #[inline] - unsafe fn to_jsval(&self, cx: *mut JSContext, mut rval: MutableHandleValue) { - rooted!(in(cx) let js_object = JS_NewPlainObject(cx)); - assert!(!js_object.handle().is_null()); - - rooted!(in(cx) let mut js_value = UndefinedValue()); - for (key, value) in &self.map { - let key = key.encode_utf16().collect::<Vec<_>>(); - value.to_jsval(cx, js_value.handle_mut()); - - assert!(JS_DefineUCProperty2( - cx, - js_object.handle(), - key.as_ptr(), - key.len(), - js_value.handle(), - JSPROP_ENUMERATE as u32 - )); - } - - rval.set(ObjectValue(js_object.handle().get())); - } -} diff --git a/components/script/dom/bindings/record.rs b/components/script/dom/bindings/record.rs new file mode 100644 index 00000000000..86453b59fc3 --- /dev/null +++ b/components/script/dom/bindings/record.rs @@ -0,0 +1,199 @@ +/* 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 https://mozilla.org/MPL/2.0/. */ + +//! The `Record` (open-ended dictionary) type. + +use crate::dom::bindings::conversions::jsid_to_string; +use crate::dom::bindings::str::{ByteString, DOMString, USVString}; +use indexmap::IndexMap; +use js::conversions::{ConversionResult, FromJSValConvertible, ToJSValConvertible}; +use js::jsapi::HandleId as RawHandleId; +use js::jsapi::JSContext; +use js::jsapi::JS_NewPlainObject; +use js::jsapi::PropertyDescriptor; +use js::jsapi::JSITER_HIDDEN; +use js::jsapi::JSITER_OWNONLY; +use js::jsapi::JSITER_SYMBOLS; +use js::jsapi::JSPROP_ENUMERATE; +use js::jsval::ObjectValue; +use js::jsval::UndefinedValue; +use js::rust::wrappers::GetPropertyKeys; +use js::rust::wrappers::JS_DefineUCProperty2; +use js::rust::wrappers::JS_GetOwnPropertyDescriptorById; +use js::rust::wrappers::JS_GetPropertyById; +use js::rust::wrappers::JS_IdToValue; +use js::rust::HandleId; +use js::rust::HandleValue; +use js::rust::IdVector; +use js::rust::MutableHandleValue; +use std::cmp::Eq; +use std::hash::Hash; +use std::marker::Sized; +use std::ops::Deref; + +pub trait RecordKey: Eq + Hash + Sized { + fn to_utf16_vec(&self) -> Vec<u16>; + unsafe fn from_id(cx: *mut JSContext, id: HandleId) -> Result<ConversionResult<Self>, ()>; +} + +impl RecordKey for DOMString { + fn to_utf16_vec(&self) -> Vec<u16> { + self.encode_utf16().collect::<Vec<_>>() + } + + unsafe fn from_id(cx: *mut JSContext, id: HandleId) -> Result<ConversionResult<Self>, ()> { + match jsid_to_string(cx, id) { + Some(s) => Ok(ConversionResult::Success(s)), + None => Ok(ConversionResult::Failure("Failed to get DOMString".into())), + } + } +} + +impl RecordKey for USVString { + fn to_utf16_vec(&self) -> Vec<u16> { + self.0.encode_utf16().collect::<Vec<_>>() + } + + unsafe fn from_id(cx: *mut JSContext, id: HandleId) -> Result<ConversionResult<Self>, ()> { + rooted!(in(cx) let mut jsid_value = UndefinedValue()); + let raw_id: RawHandleId = id.into(); + JS_IdToValue(cx, *raw_id.ptr, jsid_value.handle_mut()); + + USVString::from_jsval(cx, jsid_value.handle(), ()) + } +} + +impl RecordKey for ByteString { + fn to_utf16_vec(&self) -> Vec<u16> { + self.iter().map(|&x| x as u16).collect::<Vec<u16>>() + } + + unsafe fn from_id(cx: *mut JSContext, id: HandleId) -> Result<ConversionResult<Self>, ()> { + rooted!(in(cx) let mut jsid_value = UndefinedValue()); + let raw_id: RawHandleId = id.into(); + JS_IdToValue(cx, *raw_id.ptr, jsid_value.handle_mut()); + + ByteString::from_jsval(cx, jsid_value.handle(), ()) + } +} + +/// The `Record` (open-ended dictionary) type. +#[derive(Clone, JSTraceable)] +pub struct Record<K: RecordKey, V> { + map: IndexMap<K, V>, +} + +impl<K: RecordKey, V> Record<K, V> { + /// Create an empty `Record`. + pub fn new() -> Self { + Record { + map: IndexMap::new(), + } + } +} + +impl<K: RecordKey, V> Deref for Record<K, V> { + type Target = IndexMap<K, V>; + + fn deref(&self) -> &IndexMap<K, V> { + &self.map + } +} + +impl<K, V, C> FromJSValConvertible for Record<K, V> +where + K: RecordKey, + V: FromJSValConvertible<Config = C>, + C: Clone, +{ + type Config = C; + unsafe fn from_jsval( + cx: *mut JSContext, + value: HandleValue, + config: C, + ) -> Result<ConversionResult<Self>, ()> { + if !value.is_object() { + return Ok(ConversionResult::Failure( + "Record value was not an object".into(), + )); + } + + rooted!(in(cx) let object = value.to_object()); + let ids = IdVector::new(cx); + if !GetPropertyKeys( + cx, + object.handle(), + JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, + ids.get(), + ) { + return Err(()); + } + + let mut map = IndexMap::new(); + for id in &*ids { + rooted!(in(cx) let id = *id); + rooted!(in(cx) let mut desc = PropertyDescriptor::default()); + + if !JS_GetOwnPropertyDescriptorById(cx, object.handle(), id.handle(), desc.handle_mut()) + { + return Err(()); + } + + if (JSPROP_ENUMERATE as u32) & desc.attrs == 0 { + continue; + } + + let key = match K::from_id(cx, id.handle())? { + ConversionResult::Success(key) => key, + ConversionResult::Failure(message) => { + return Ok(ConversionResult::Failure(message)) + }, + }; + + rooted!(in(cx) let mut property = UndefinedValue()); + if !JS_GetPropertyById(cx, object.handle(), id.handle(), property.handle_mut()) { + return Err(()); + } + + let property = match V::from_jsval(cx, property.handle(), config.clone())? { + ConversionResult::Success(property) => property, + ConversionResult::Failure(message) => { + return Ok(ConversionResult::Failure(message)) + }, + }; + map.insert(key, property); + } + + Ok(ConversionResult::Success(Record { map: map })) + } +} + +impl<K, V> ToJSValConvertible for Record<K, V> +where + K: RecordKey, + V: ToJSValConvertible, +{ + #[inline] + unsafe fn to_jsval(&self, cx: *mut JSContext, mut rval: MutableHandleValue) { + rooted!(in(cx) let js_object = JS_NewPlainObject(cx)); + assert!(!js_object.handle().is_null()); + + rooted!(in(cx) let mut js_value = UndefinedValue()); + for (key, value) in &self.map { + let key = key.to_utf16_vec(); + value.to_jsval(cx, js_value.handle_mut()); + + assert!(JS_DefineUCProperty2( + cx, + js_object.handle(), + key.as_ptr(), + key.len(), + js_value.handle(), + JSPROP_ENUMERATE as u32 + )); + } + + rval.set(ObjectValue(js_object.handle().get())); + } +} diff --git a/components/script/dom/bindings/str.rs b/components/script/dom/bindings/str.rs index ad4ce8bc621..38091fcf19e 100644 --- a/components/script/dom/bindings/str.rs +++ b/components/script/dom/bindings/str.rs @@ -74,7 +74,7 @@ impl ops::Deref for ByteString { /// A string that is constructed from a UCS-2 buffer by replacing invalid code /// points with the replacement character. -#[derive(Clone, Default, Eq, MallocSizeOf, Ord, PartialEq, PartialOrd)] +#[derive(Clone, Default, Eq, Hash, MallocSizeOf, Ord, PartialEq, PartialOrd)] pub struct USVString(pub String); impl Borrow<str> for USVString { |