Skip to main content

script_bindings/
conversions.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use std::{ptr, slice};
6
7use js::conversions::{
8    ConversionResult, FromJSValConvertible, ToJSValConvertible, jsstr_to_string,
9};
10use js::error::throw_type_error;
11use js::glue::{
12    GetProxyHandlerExtra, GetProxyReservedSlot, IsProxyHandlerFamily, IsWrapper,
13    JS_GetReservedSlot, UnwrapObjectDynamic,
14};
15use js::jsapi::{
16    Heap, IsWindowProxy, JS_DeprecatedStringHasLatin1Chars, JS_NewStringCopyN, JSContext, JSObject,
17};
18use js::jsval::{ObjectValue, StringValue, UndefinedValue};
19use js::rust::wrappers2::{
20    IsArrayObject, JS_GetLatin1StringCharsAndLength, JS_GetTwoByteStringCharsAndLength,
21};
22use js::rust::{
23    HandleId, HandleValue, MutableHandleValue, ToString, get_object_class, is_dom_class,
24    is_dom_object, maybe_wrap_value,
25};
26use keyboard_types::Modifiers;
27use num_traits::Float;
28
29use crate::JSTraceable;
30use crate::codegen::GenericBindings::EventModifierInitBinding::EventModifierInit;
31use crate::inheritance::Castable;
32use crate::num::Finite;
33use crate::reflector::{DomObject, Reflector};
34use crate::root::DomRoot;
35use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
36use crate::str::{ByteString, DOMString, USVString};
37use crate::trace::RootedTraceableBox;
38use crate::utils::{DOMClass, DOMJSClass};
39
40/// A safe wrapper for `ToJSValConvertible`.
41pub trait SafeToJSValConvertible {
42    fn safe_to_jsval(&self, cx: SafeJSContext, rval: MutableHandleValue, can_gc: CanGc);
43}
44
45impl<T: ToJSValConvertible + ?Sized> SafeToJSValConvertible for T {
46    fn safe_to_jsval(&self, cx: SafeJSContext, rval: MutableHandleValue, _can_gc: CanGc) {
47        unsafe { self.to_jsval(*cx, rval) };
48    }
49}
50
51/// A trait to check whether a given `JSObject` implements an IDL interface.
52pub trait IDLInterface {
53    /// Returns whether the given DOM class derives that interface.
54    fn derives(_: &'static DOMClass) -> bool;
55
56    /// First prototype ID in the DFS-ordered range for this interface and its descendants.
57    const PROTO_FIRST: u16 = 0;
58    /// Last prototype ID in the DFS-ordered range for this interface and its descendants.
59    const PROTO_LAST: u16 = u16::MAX;
60}
61
62/// A trait to mark an IDL interface as deriving from another one.
63pub trait DerivedFrom<T: Castable>: Castable {}
64
65// http://heycam.github.io/webidl/#es-USVString
66impl ToJSValConvertible for USVString {
67    unsafe fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) {
68        self.0.to_jsval(cx, rval);
69    }
70}
71
72/// Behavior for stringification of `JSVal`s.
73#[derive(Clone, PartialEq)]
74pub enum StringificationBehavior {
75    /// Convert `null` to the string `"null"`.
76    Default,
77    /// Convert `null` to the empty string.
78    Empty,
79}
80
81/// A safe wrapper for `FromJSValConvertible`.
82pub trait SafeFromJSValConvertible: Sized {
83    type Config;
84
85    #[allow(clippy::result_unit_err)] // Type definition depends on mozjs
86    fn safe_from_jsval(
87        cx: SafeJSContext,
88        value: HandleValue,
89        option: Self::Config,
90        _can_gc: CanGc,
91    ) -> Result<ConversionResult<Self>, ()>;
92}
93
94impl<T: FromJSValConvertible> SafeFromJSValConvertible for T {
95    type Config = <T as FromJSValConvertible>::Config;
96
97    fn safe_from_jsval(
98        cx: SafeJSContext,
99        value: HandleValue,
100        option: Self::Config,
101        _can_gc: CanGc,
102    ) -> Result<ConversionResult<Self>, ()> {
103        unsafe { T::from_jsval(*cx, value, option) }
104    }
105}
106
107// https://heycam.github.io/webidl/#es-DOMString
108impl FromJSValConvertible for DOMString {
109    type Config = StringificationBehavior;
110    unsafe fn from_jsval(
111        _cx: *mut JSContext,
112        value: HandleValue,
113        null_behavior: StringificationBehavior,
114    ) -> Result<ConversionResult<DOMString>, ()> {
115        // TODO https://github.com/servo/mozjs/issues/749
116        let mut cx = unsafe { crate::script_runtime::temp_cx() };
117        FromJSValConvertible::safe_from_jsval(&mut cx, value, null_behavior)
118    }
119
120    fn safe_from_jsval(
121        cx: &mut js::context::JSContext,
122        value: HandleValue,
123        null_behavior: StringificationBehavior,
124    ) -> Result<ConversionResult<DOMString>, ()> {
125        if null_behavior == StringificationBehavior::Empty && value.get().is_null() {
126            Ok(ConversionResult::Success(DOMString::new()))
127        } else {
128            match DOMString::from_js_string(cx, value) {
129                Ok(domstring) => Ok(ConversionResult::Success(domstring)),
130                Err(_) => Err(()),
131            }
132        }
133    }
134}
135
136// http://heycam.github.io/webidl/#es-USVString
137impl FromJSValConvertible for USVString {
138    type Config = ();
139    unsafe fn from_jsval(
140        _cx: *mut JSContext,
141        value: HandleValue,
142        _: (),
143    ) -> Result<ConversionResult<USVString>, ()> {
144        // TODO https://github.com/servo/mozjs/issues/749
145        let mut cx = unsafe { crate::script_runtime::temp_cx() };
146        FromJSValConvertible::safe_from_jsval(&mut cx, value, ())
147    }
148
149    fn safe_from_jsval(
150        cx: &mut js::context::JSContext,
151        value: HandleValue,
152        _: (),
153    ) -> Result<ConversionResult<USVString>, ()> {
154        let Some(jsstr) = ptr::NonNull::new(unsafe { ToString(cx.raw_cx(), value) }) else {
155            debug!("ToString failed");
156            return Err(());
157        };
158
159        // FIXME(ajeffrey): Convert directly from DOMString to USVString
160        Ok(ConversionResult::Success(USVString(unsafe {
161            jsstr_to_string(cx.raw_cx(), jsstr)
162        })))
163    }
164}
165
166// http://heycam.github.io/webidl/#es-ByteString
167impl ToJSValConvertible for ByteString {
168    unsafe fn to_jsval(&self, cx: *mut JSContext, mut rval: MutableHandleValue) {
169        let jsstr = JS_NewStringCopyN(
170            cx,
171            self.as_ptr() as *const libc::c_char,
172            self.len() as libc::size_t,
173        );
174        if jsstr.is_null() {
175            panic!("JS_NewStringCopyN failed");
176        }
177        rval.set(StringValue(&*jsstr));
178    }
179}
180
181// http://heycam.github.io/webidl/#es-ByteString
182impl FromJSValConvertible for ByteString {
183    type Config = ();
184    unsafe fn from_jsval(
185        _cx: *mut JSContext,
186        value: HandleValue,
187        _option: (),
188    ) -> Result<ConversionResult<ByteString>, ()> {
189        // TODO https://github.com/servo/mozjs/issues/749
190        let mut cx = unsafe { crate::script_runtime::temp_cx() };
191        FromJSValConvertible::safe_from_jsval(&mut cx, value, ())
192    }
193
194    fn safe_from_jsval(
195        cx: &mut js::context::JSContext,
196        value: HandleValue,
197        _option: (),
198    ) -> Result<ConversionResult<ByteString>, ()> {
199        unsafe {
200            let string = ToString(cx.raw_cx(), value);
201            if string.is_null() {
202                debug!("ToString failed");
203                return Err(());
204            }
205
206            let latin1 = JS_DeprecatedStringHasLatin1Chars(string);
207            if latin1 {
208                let mut length = 0;
209                let chars = JS_GetLatin1StringCharsAndLength(cx, string, &mut length);
210                assert!(!chars.is_null());
211
212                let char_slice = slice::from_raw_parts(chars as *mut u8, length);
213                return Ok(ConversionResult::Success(ByteString::new(
214                    char_slice.to_vec(),
215                )));
216            }
217
218            let mut length = 0;
219            let chars = JS_GetTwoByteStringCharsAndLength(cx, string, &mut length);
220            let char_vec = slice::from_raw_parts(chars, length);
221
222            if char_vec.iter().any(|&c| c > 0xFF) {
223                throw_type_error(cx.raw_cx(), c"Invalid ByteString");
224                Err(())
225            } else {
226                Ok(ConversionResult::Success(ByteString::new(
227                    char_vec.iter().map(|&c| c as u8).collect(),
228                )))
229            }
230        }
231    }
232}
233
234impl<T> ToJSValConvertible for Reflector<T> {
235    unsafe fn to_jsval(&self, cx: *mut JSContext, mut rval: MutableHandleValue) {
236        let obj = self.get_jsobject().get();
237        assert!(!obj.is_null());
238        rval.set(ObjectValue(obj));
239        maybe_wrap_value(cx, rval);
240    }
241}
242
243impl<T: DomObject + IDLInterface> FromJSValConvertible for DomRoot<T> {
244    type Config = ();
245
246    unsafe fn from_jsval(
247        _cx: *mut JSContext,
248        value: HandleValue,
249        _config: Self::Config,
250    ) -> Result<ConversionResult<DomRoot<T>>, ()> {
251        // TODO https://github.com/servo/mozjs/issues/749
252        let mut cx = unsafe { crate::script_runtime::temp_cx() };
253        FromJSValConvertible::safe_from_jsval(&mut cx, value, ())
254    }
255
256    fn safe_from_jsval(
257        cx: &mut js::context::JSContext,
258        value: HandleValue,
259        _config: Self::Config,
260    ) -> Result<ConversionResult<DomRoot<T>>, ()> {
261        Ok(match root_from_handlevalue(value, cx.into()) {
262            Ok(result) => ConversionResult::Success(result),
263            Err(()) => ConversionResult::Failure(c"value is not an object".into()),
264        })
265    }
266}
267
268impl<T: DomObject> ToJSValConvertible for DomRoot<T> {
269    unsafe fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) {
270        self.reflector().to_jsval(cx, rval);
271    }
272}
273
274/// Get the `DOMClass` from `obj`, or `Err(())` if `obj` is not a DOM object.
275///
276/// # Safety
277/// obj must point to a valid, non-null JS object.
278#[allow(clippy::result_unit_err)]
279pub unsafe fn get_dom_class(obj: *mut JSObject) -> Result<&'static DOMClass, ()> {
280    let clasp = get_object_class(obj);
281    if is_dom_class(&*clasp) {
282        trace!("plain old dom object");
283        let domjsclass: *const DOMJSClass = clasp as *const DOMJSClass;
284        return Ok(&(*domjsclass).dom_class);
285    }
286    if is_dom_proxy(obj) {
287        trace!("proxy dom object");
288        let dom_class: *const DOMClass = GetProxyHandlerExtra(obj) as *const DOMClass;
289        if dom_class.is_null() {
290            return Err(());
291        }
292        return Ok(&*dom_class);
293    }
294    trace!("not a dom object");
295    Err(())
296}
297
298/// Returns whether `obj` is a DOM object implemented as a proxy.
299///
300/// # Safety
301/// obj must point to a valid, non-null JS object.
302pub unsafe fn is_dom_proxy(obj: *mut JSObject) -> bool {
303    unsafe {
304        let clasp = get_object_class(obj);
305        ((*clasp).flags & js::JSCLASS_IS_PROXY) != 0 && IsProxyHandlerFamily(obj)
306    }
307}
308
309/// The index of the slot wherein a pointer to the reflected DOM object is
310/// stored for non-proxy bindings.
311// We use slot 0 for holding the raw object.  This is safe for both
312// globals and non-globals.
313pub const DOM_OBJECT_SLOT: u32 = 0;
314
315/// Get the private pointer of a DOM object from a given reflector.
316///
317/// # Safety
318/// obj must point to a valid non-null JS object.
319pub unsafe fn private_from_object(obj: *mut JSObject) -> *const libc::c_void {
320    let mut value = UndefinedValue();
321    if is_dom_object(obj) {
322        JS_GetReservedSlot(obj, DOM_OBJECT_SLOT, &mut value);
323    } else {
324        debug_assert!(is_dom_proxy(obj));
325        GetProxyReservedSlot(obj, 0, &mut value);
326    };
327    if value.is_undefined() {
328        ptr::null()
329    } else {
330        value.to_private()
331    }
332}
333
334pub enum PrototypeCheck {
335    Derive(fn(&'static DOMClass) -> bool),
336    Depth { depth: usize, proto_id: u16 },
337}
338
339/// Get a `*const libc::c_void` for the given DOM object, unwrapping any
340/// wrapper around it first, and checking if the object is of the correct type.
341///
342/// Returns Err(()) if `obj` is an opaque security wrapper or if the object is
343/// not an object for a DOM object of the given type (as defined by the
344/// proto_id and proto_depth).
345///
346/// # Safety
347/// obj must point to a valid, non-null JS object.
348/// cx must point to a valid, non-null JS context.
349#[inline]
350#[allow(clippy::result_unit_err)]
351pub unsafe fn private_from_proto_check(
352    mut obj: *mut JSObject,
353    cx: *mut JSContext,
354    proto_check: PrototypeCheck,
355) -> Result<*const libc::c_void, ()> {
356    let dom_class = get_dom_class(obj).or_else(|_| {
357        if IsWrapper(obj) {
358            trace!("found wrapper");
359            obj = UnwrapObjectDynamic(obj, cx, /* stopAtWindowProxy = */ false);
360            if obj.is_null() {
361                trace!("unwrapping security wrapper failed");
362                Err(())
363            } else {
364                assert!(!IsWrapper(obj));
365                trace!("unwrapped successfully");
366                get_dom_class(obj)
367            }
368        } else {
369            trace!("not a dom wrapper");
370            Err(())
371        }
372    })?;
373
374    let prototype_matches = match proto_check {
375        PrototypeCheck::Derive(f) => (f)(dom_class),
376        PrototypeCheck::Depth { depth, proto_id } => {
377            dom_class.interface_chain[depth] as u16 == proto_id
378        },
379    };
380
381    if prototype_matches {
382        trace!("good prototype");
383        Ok(private_from_object(obj))
384    } else {
385        trace!("bad prototype");
386        Err(())
387    }
388}
389
390/// Get a `*const T` for a DOM object accessible from a `JSObject`.
391///
392/// # Safety
393/// obj must point to a valid, non-null JS object.
394/// cx must point to a valid, non-null JS context.
395#[allow(clippy::result_unit_err)]
396pub unsafe fn native_from_object<T>(obj: *mut JSObject, cx: *mut JSContext) -> Result<*const T, ()>
397where
398    T: DomObject + IDLInterface,
399{
400    unsafe {
401        private_from_proto_check(obj, cx, PrototypeCheck::Derive(T::derives))
402            .map(|ptr| ptr as *const T)
403    }
404}
405
406/// Get a `DomRoot<T>` for the given DOM object, unwrapping any wrapper
407/// around it first, and checking if the object is of the correct type.
408///
409/// Returns Err(()) if `obj` is an opaque security wrapper or if the object is
410/// not a reflector for a DOM object of the given type (as defined by the
411/// proto_id and proto_depth).
412///
413/// # Safety
414/// obj must point to a valid, non-null JS object.
415/// cx must point to a valid, non-null JS context.
416#[allow(clippy::result_unit_err)]
417pub unsafe fn root_from_object<T>(obj: *mut JSObject, cx: *mut JSContext) -> Result<DomRoot<T>, ()>
418where
419    T: DomObject + IDLInterface,
420{
421    native_from_object(obj, cx).map(|ptr| unsafe { DomRoot::from_ref(&*ptr) })
422}
423
424/// Get a `DomRoot<T>` for a DOM object accessible from a `HandleValue`.
425/// Caller is responsible for throwing a JS exception if needed in case of error.
426///
427/// # Safety
428/// cx must point to a valid, non-null JS context.
429#[allow(clippy::result_unit_err)]
430pub fn root_from_handlevalue<T>(v: HandleValue, cx: SafeJSContext) -> Result<DomRoot<T>, ()>
431where
432    T: DomObject + IDLInterface,
433{
434    if !v.get().is_object() {
435        return Err(());
436    }
437    #[expect(unsafe_code)]
438    unsafe {
439        root_from_object(v.get().to_object(), *cx)
440    }
441}
442
443/// Convert `id` to a `DOMString`. Returns `None` if `id` is not a string or
444/// integer.
445///
446/// Handling of invalid UTF-16 in strings depends on the relevant option.
447///
448/// # Safety
449/// - cx must point to a non-null, valid JSContext instance.
450pub fn jsid_to_string(cx: &mut js::context::JSContext, id: HandleId) -> Option<DOMString> {
451    let id_raw = *id;
452    if id_raw.is_string() {
453        let jsstr = ptr::NonNull::new(id_raw.to_string()).unwrap();
454        return Some(unsafe { jsstr_to_string(cx.raw_cx(), jsstr) }.into());
455    }
456
457    if id_raw.is_int() {
458        return Some(id_raw.to_int().to_string().into());
459    }
460
461    None
462}
463
464impl<T: Float + ToJSValConvertible> ToJSValConvertible for Finite<T> {
465    #[inline]
466    unsafe fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) {
467        let value = **self;
468        value.to_jsval(cx, rval);
469    }
470}
471
472impl<T: Float + FromJSValConvertible<Config = ()>> FromJSValConvertible for Finite<T> {
473    type Config = ();
474
475    unsafe fn from_jsval(
476        _cx: *mut JSContext,
477        value: HandleValue,
478        option: (),
479    ) -> Result<ConversionResult<Finite<T>>, ()> {
480        // TODO https://github.com/servo/mozjs/issues/749
481        let mut cx = unsafe { crate::script_runtime::temp_cx() };
482        FromJSValConvertible::safe_from_jsval(&mut cx, value, option)
483    }
484
485    fn safe_from_jsval(
486        cx: &mut js::context::JSContext,
487        value: HandleValue,
488        option: (),
489    ) -> Result<ConversionResult<Finite<T>>, ()> {
490        let result = match FromJSValConvertible::safe_from_jsval(cx, value, option)? {
491            ConversionResult::Success(v) => v,
492            ConversionResult::Failure(error) => {
493                // FIXME(emilio): Why throwing instead of propagating the error?
494                unsafe { throw_type_error(cx.raw_cx(), &error) };
495                return Err(());
496            },
497        };
498        match Finite::new(result) {
499            Some(v) => Ok(ConversionResult::Success(v)),
500            None => {
501                unsafe {
502                    throw_type_error(
503                        cx.raw_cx(),
504                        c"this argument is not a finite floating-point value",
505                    )
506                };
507                Err(())
508            },
509        }
510    }
511}
512
513/// Get a `*const libc::c_void` for the given DOM object, unless it is a DOM
514/// wrapper, and checking if the object is of the correct type.
515///
516/// Returns Err(()) if `obj` is a wrapper or if the object is not an object
517/// for a DOM object of the given type (as defined by the proto_id and proto_depth).
518#[inline]
519#[allow(clippy::result_unit_err)]
520unsafe fn private_from_proto_check_static(
521    obj: *mut JSObject,
522    proto_check: fn(&'static DOMClass) -> bool,
523) -> Result<*const libc::c_void, ()> {
524    let dom_class = get_dom_class(obj).map_err(|_| ())?;
525    if proto_check(dom_class) {
526        trace!("good prototype");
527        Ok(private_from_object(obj))
528    } else {
529        trace!("bad prototype");
530        Err(())
531    }
532}
533
534/// Get a `*const T` for a DOM object accessible from a `JSObject`, where the DOM object
535/// is guaranteed not to be a wrapper.
536///
537/// # Safety
538/// `obj` must point to a valid, non-null JSObject.
539#[allow(clippy::result_unit_err)]
540pub unsafe fn native_from_object_static<T>(obj: *mut JSObject) -> Result<*const T, ()>
541where
542    T: DomObject + IDLInterface,
543{
544    private_from_proto_check_static(obj, T::derives).map(|ptr| ptr as *const T)
545}
546
547/// Get a `*const T` for a DOM object accessible from a `HandleValue`.
548/// Caller is responsible for throwing a JS exception if needed in case of error.
549///
550/// # Safety
551/// `cx` must point to a valid, non-null JSContext.
552#[allow(clippy::result_unit_err)]
553pub fn native_from_handlevalue<T>(v: HandleValue, cx: SafeJSContext) -> Result<*const T, ()>
554where
555    T: DomObject + IDLInterface,
556{
557    if !v.get().is_object() {
558        return Err(());
559    }
560
561    #[expect(unsafe_code)]
562    unsafe {
563        native_from_object(v.get().to_object(), *cx)
564    }
565}
566
567impl<T: ToJSValConvertible + JSTraceable> ToJSValConvertible for RootedTraceableBox<T> {
568    #[inline]
569    unsafe fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) {
570        let value = &**self;
571        value.to_jsval(cx, rval);
572    }
573}
574
575impl<T> FromJSValConvertible for RootedTraceableBox<Heap<T>>
576where
577    T: FromJSValConvertible + js::rust::GCMethods + Copy,
578    Heap<T>: JSTraceable + Default,
579{
580    type Config = T::Config;
581
582    unsafe fn from_jsval(
583        _cx: *mut JSContext,
584        value: HandleValue,
585        config: Self::Config,
586    ) -> Result<ConversionResult<Self>, ()> {
587        // TODO https://github.com/servo/mozjs/issues/749
588        let mut cx = unsafe { crate::script_runtime::temp_cx() };
589        FromJSValConvertible::safe_from_jsval(&mut cx, value, config)
590    }
591
592    fn safe_from_jsval(
593        cx: &mut js::context::JSContext,
594        value: HandleValue,
595        config: Self::Config,
596    ) -> Result<ConversionResult<Self>, ()> {
597        T::safe_from_jsval(cx, value, config).map(|result| match result {
598            ConversionResult::Success(inner) => {
599                ConversionResult::Success(RootedTraceableBox::from_box(Heap::boxed(inner)))
600            },
601            ConversionResult::Failure(msg) => ConversionResult::Failure(msg),
602        })
603    }
604}
605
606/// Returns whether `value` is an array-like object (Array, FileList,
607/// HTMLCollection, HTMLFormControlsCollection, HTMLOptionsCollection,
608/// NodeList, DOMTokenList).
609pub fn is_array_like<D: crate::DomTypes>(
610    cx: &mut js::context::JSContext,
611    value: HandleValue,
612) -> bool {
613    let mut is_array = false;
614    assert!(unsafe { IsArrayObject(cx, value, &mut is_array) });
615    if is_array {
616        return true;
617    }
618
619    let object: *mut JSObject = match FromJSValConvertible::safe_from_jsval(cx, value, ()).unwrap()
620    {
621        ConversionResult::Success(object) => object,
622        _ => return false,
623    };
624
625    unsafe {
626        // TODO: HTMLAllCollection
627        if root_from_object::<D::DOMTokenList>(object, cx.raw_cx()).is_ok() {
628            return true;
629        }
630        if root_from_object::<D::FileList>(object, cx.raw_cx()).is_ok() {
631            return true;
632        }
633        if root_from_object::<D::HTMLCollection>(object, cx.raw_cx()).is_ok() {
634            return true;
635        }
636        if root_from_object::<D::HTMLFormControlsCollection>(object, cx.raw_cx()).is_ok() {
637            return true;
638        }
639        if root_from_object::<D::HTMLOptionsCollection>(object, cx.raw_cx()).is_ok() {
640            return true;
641        }
642        if root_from_object::<D::NodeList>(object, cx.raw_cx()).is_ok() {
643            return true;
644        }
645    }
646
647    false
648}
649
650/// Get a `DomRoot<T>` for a WindowProxy accessible from a `HandleValue`.
651/// Caller is responsible for throwing a JS exception if needed in case of error.
652pub(crate) unsafe fn windowproxy_from_handlevalue<D: crate::DomTypes>(
653    v: HandleValue,
654    _cx: SafeJSContext,
655) -> Result<DomRoot<D::WindowProxy>, ()> {
656    if !v.get().is_object() {
657        return Err(());
658    }
659    let object = v.get().to_object();
660    if !IsWindowProxy(object) {
661        return Err(());
662    }
663    let mut value = UndefinedValue();
664    GetProxyReservedSlot(object, 0, &mut value);
665    let ptr = value.to_private() as *const D::WindowProxy;
666    Ok(DomRoot::from_ref(&*ptr))
667}
668
669#[allow(deprecated)]
670impl<D: crate::DomTypes> EventModifierInit<D> {
671    pub fn modifiers(&self) -> Modifiers {
672        let mut modifiers = Modifiers::empty();
673        if self.altKey {
674            modifiers.insert(Modifiers::ALT);
675        }
676        if self.ctrlKey {
677            modifiers.insert(Modifiers::CONTROL);
678        }
679        if self.shiftKey {
680            modifiers.insert(Modifiers::SHIFT);
681        }
682        if self.metaKey {
683            modifiers.insert(Modifiers::META);
684        }
685        if self.keyModifierStateAltGraph {
686            modifiers.insert(Modifiers::ALT_GRAPH);
687        }
688        if self.keyModifierStateCapsLock {
689            modifiers.insert(Modifiers::CAPS_LOCK);
690        }
691        if self.keyModifierStateFn {
692            modifiers.insert(Modifiers::FN);
693        }
694        if self.keyModifierStateFnLock {
695            modifiers.insert(Modifiers::FN_LOCK);
696        }
697        if self.keyModifierStateHyper {
698            modifiers.insert(Modifiers::HYPER);
699        }
700        if self.keyModifierStateNumLock {
701            modifiers.insert(Modifiers::NUM_LOCK);
702        }
703        if self.keyModifierStateScrollLock {
704            modifiers.insert(Modifiers::SCROLL_LOCK);
705        }
706        if self.keyModifierStateSuper {
707            modifiers.insert(Modifiers::SUPER);
708        }
709        if self.keyModifierStateSymbol {
710            modifiers.insert(Modifiers::SYMBOL);
711        }
712        if self.keyModifierStateSymbolLock {
713            modifiers.insert(Modifiers::SYMBOL_LOCK);
714        }
715        modifiers
716    }
717}