1use std::cmp::Eq;
8use std::hash::Hash;
9use std::marker::Sized;
10use std::ops::{Deref, DerefMut};
11
12use indexmap::IndexMap;
13use js::context::{JSContext, RawJSContext};
14use js::conversions::{ConversionResult, FromJSValConvertible, ToJSValConvertible};
15use js::jsapi::{
16 JS_NewPlainObject, JSITER_HIDDEN, JSITER_OWNONLY, JSITER_SYMBOLS, JSPROP_ENUMERATE,
17 PropertyDescriptor,
18};
19use js::jsval::{ObjectValue, UndefinedValue};
20use js::rust::wrappers::JS_DefineUCProperty2;
21use js::rust::wrappers2::{
22 GetPropertyKeys, JS_GetOwnPropertyDescriptorById, JS_GetPropertyById, JS_IdToValue,
23};
24use js::rust::{HandleId, HandleValue, IdVector, MutableHandleValue};
25
26use crate::conversions::jsid_to_string;
27use crate::str::{ByteString, DOMString, USVString};
28
29pub trait RecordKey: Eq + Hash + Sized {
30 fn to_utf16_vec(&self) -> Vec<u16>;
31
32 #[allow(clippy::result_unit_err)]
34 fn from_id(cx: &mut JSContext, id: HandleId) -> Result<ConversionResult<Self>, ()>;
35}
36
37impl RecordKey for DOMString {
38 fn to_utf16_vec(&self) -> Vec<u16> {
39 self.str().encode_utf16().collect::<Vec<_>>()
40 }
41
42 fn from_id(cx: &mut JSContext, id: HandleId) -> Result<ConversionResult<Self>, ()> {
43 match jsid_to_string(cx, id) {
44 Some(s) => Ok(ConversionResult::Success(s)),
45 None => Ok(ConversionResult::Failure(c"Failed to get DOMString".into())),
46 }
47 }
48}
49
50impl RecordKey for USVString {
51 fn to_utf16_vec(&self) -> Vec<u16> {
52 self.0.encode_utf16().collect::<Vec<_>>()
53 }
54
55 fn from_id(cx: &mut JSContext, id: HandleId) -> Result<ConversionResult<Self>, ()> {
56 rooted!(&in(cx) let mut jsid_value = UndefinedValue());
57 unsafe { JS_IdToValue(cx, *id.as_ref(cx), jsid_value.handle_mut()) };
58
59 USVString::safe_from_jsval(cx, jsid_value.handle(), ())
60 }
61}
62
63impl RecordKey for ByteString {
64 fn to_utf16_vec(&self) -> Vec<u16> {
65 self.iter().map(|&x| x as u16).collect::<Vec<u16>>()
66 }
67
68 fn from_id(cx: &mut JSContext, id: HandleId) -> Result<ConversionResult<Self>, ()> {
69 rooted!(&in(cx) let mut jsid_value = UndefinedValue());
70 unsafe { JS_IdToValue(cx, *id.as_ref(cx), jsid_value.handle_mut()) };
71
72 ByteString::safe_from_jsval(cx, jsid_value.handle(), ())
73 }
74}
75
76#[derive(Clone, JSTraceable)]
78pub struct Record<K: RecordKey, V> {
79 #[custom_trace]
80 map: IndexMap<K, V>,
81}
82
83impl<K: RecordKey, V> Record<K, V> {
84 pub fn new() -> Self {
86 Record {
87 map: IndexMap::new(),
88 }
89 }
90}
91
92impl<K: RecordKey, V> Deref for Record<K, V> {
93 type Target = IndexMap<K, V>;
94
95 fn deref(&self) -> &Self::Target {
96 &self.map
97 }
98}
99
100impl<K: RecordKey, V> DerefMut for Record<K, V> {
101 fn deref_mut(&mut self) -> &mut Self::Target {
102 &mut self.map
103 }
104}
105
106impl<K, V, C> FromJSValConvertible for Record<K, V>
107where
108 K: RecordKey,
109 V: FromJSValConvertible<Config = C>,
110 C: Clone,
111{
112 type Config = C;
113 unsafe fn from_jsval(
114 _cx: *mut RawJSContext,
115 value: HandleValue,
116 config: C,
117 ) -> Result<ConversionResult<Self>, ()> {
118 let mut cx = unsafe { crate::script_runtime::temp_cx() };
120 FromJSValConvertible::safe_from_jsval(&mut cx, value, config)
121 }
122
123 fn safe_from_jsval(
124 cx: &mut JSContext,
125 value: HandleValue,
126 config: C,
127 ) -> Result<ConversionResult<Self>, ()> {
128 if !value.is_object() {
129 return Ok(ConversionResult::Failure(
130 c"Record value was not an object".into(),
131 ));
132 }
133
134 rooted!(&in(cx) let object = value.to_object());
135 let mut ids = unsafe { IdVector::new(cx.raw_cx()) };
136 if unsafe {
137 !GetPropertyKeys(
138 cx,
139 object.handle(),
140 JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS,
141 ids.handle_mut(),
142 )
143 } {
144 return Err(());
145 }
146
147 let mut map = IndexMap::new();
148 for id in &*ids {
149 rooted!(&in(cx) let id = *id);
150 rooted!(&in(cx) let mut desc = PropertyDescriptor::default());
151
152 let mut is_none = false;
153 if unsafe {
154 !JS_GetOwnPropertyDescriptorById(
155 cx,
156 object.handle(),
157 id.handle(),
158 desc.handle_mut(),
159 &mut is_none,
160 )
161 } {
162 return Err(());
163 }
164
165 if !desc.enumerable_() {
166 continue;
167 }
168
169 let key = match K::from_id(cx, id.handle())? {
170 ConversionResult::Success(key) => key,
171 ConversionResult::Failure(message) => {
172 return Ok(ConversionResult::Failure(message));
173 },
174 };
175
176 rooted!(&in(cx) let mut property = UndefinedValue());
177 if unsafe {
178 !JS_GetPropertyById(cx, object.handle(), id.handle(), property.handle_mut())
179 } {
180 return Err(());
181 }
182
183 let property = match V::safe_from_jsval(cx, property.handle(), config.clone())? {
184 ConversionResult::Success(property) => property,
185 ConversionResult::Failure(message) => {
186 return Ok(ConversionResult::Failure(message));
187 },
188 };
189 map.insert(key, property);
190 }
191
192 Ok(ConversionResult::Success(Record { map }))
193 }
194}
195
196impl<K, V> ToJSValConvertible for Record<K, V>
197where
198 K: RecordKey,
199 V: ToJSValConvertible,
200{
201 #[inline]
202 unsafe fn to_jsval(&self, cx: *mut RawJSContext, mut rval: MutableHandleValue) {
203 rooted!(in(cx) let js_object = JS_NewPlainObject(cx));
204 assert!(!js_object.handle().is_null());
205
206 rooted!(in(cx) let mut js_value = UndefinedValue());
207 for (key, value) in &self.map {
208 let key = key.to_utf16_vec();
209 value.to_jsval(cx, js_value.handle_mut());
210
211 assert!(JS_DefineUCProperty2(
212 cx,
213 js_object.handle(),
214 key.as_ptr(),
215 key.len(),
216 js_value.handle(),
217 JSPROP_ENUMERATE as u32
218 ));
219 }
220
221 rval.set(ObjectValue(js_object.handle().get()));
222 }
223}
224
225impl<K: RecordKey, V> Default for Record<K, V> {
226 fn default() -> Self {
227 Self::new()
228 }
229}