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