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