Skip to main content

script_bindings/
wrap.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/. */
4use std::ffi::c_void;
5use std::ptr;
6
7use js::JSCLASS_IS_GLOBAL;
8use js::context::JSContext;
9use js::gc::{HandleObject, MutableHandleObject};
10use js::glue::SetProxyReservedSlot;
11use js::jsapi::{JS_SetReservedSlot, JSAutoRealm, JSClass, JSObject};
12use js::jsval::{PrivateValue, UndefinedValue};
13use js::rust::wrappers2::{
14    JS_InitializePropertiesFromCompatibleNativeObject, JS_NewObjectWithGivenProto, JS_WrapObject,
15    NewProxyObject,
16};
17use js::rust::{Handle, get_context_realm, get_object_class, get_object_realm};
18
19use crate::codegen::PrototypeList;
20use crate::conversions::DOM_OBJECT_SLOT;
21use crate::import::module::JS_GetReservedSlot;
22use crate::proxyhandler::ensure_expando_object;
23use crate::root::{DomRoot, MaybeUnreflectedDom, Root};
24use crate::utils::DOM_PROTO_UNFORGEABLE_HOLDER_SLOT;
25use crate::{DomObject, DomTypes, MutDomObject};
26
27type ProtoObjectFn = fn(&mut js::context::JSContext, HandleObject, MutableHandleObject);
28
29/// TODO: unforgeable is missing
30pub(crate) struct WrapConfig {
31    pub(crate) is_maybe_cross_origin_object: bool,
32    pub(crate) is_proxy: bool,
33    pub(crate) proxy_handler: Option<*const c_void>,
34    pub(crate) prototype_id: PrototypeList::ID,
35    pub(crate) class: Option<&'static JSClass>,
36    // this function has to be more general because we do not have the correct type for globalscope.
37    pub(crate) proto_object_fn: ProtoObjectFn,
38    pub(crate) has_legacy_unforgeable_members: bool,
39}
40
41#[cfg_attr(crown, allow(crown::unrooted_must_root))]
42pub(crate) unsafe fn wrap<T: MutDomObject, D: DomTypes>(
43    cx: &mut JSContext,
44    scope: &D::GlobalScope,
45    given_proto: Option<js::rust::Handle<*mut JSObject>>,
46    raw: Root<MaybeUnreflectedDom<T>>,
47    config: WrapConfig,
48) -> DomRoot<T> {
49    unsafe {
50        let scope = scope.reflector().get_jsobject();
51        assert!(!scope.get().is_null());
52        assert!(((*get_object_class(scope.get())).flags & JSCLASS_IS_GLOBAL) != 0);
53        let _ac = JSAutoRealm::new(cx.raw_cx(), scope.get());
54
55        rooted!(&in(cx) let mut canonical_proto = ptr::null_mut::<JSObject>());
56        (config.proto_object_fn)(cx, scope, canonical_proto.handle_mut());
57        assert!(!canonical_proto.is_null());
58
59        rooted!(&in(cx) let mut obj = ptr::null_mut::<JSObject>());
60        if config.is_proxy {
61            let handler: *const libc::c_void = config.proxy_handler.unwrap();
62
63            if config.is_maybe_cross_origin_object {
64                obj.set(NewProxyObject(
65                    cx,
66                    handler,
67                    Handle::undefined(),
68                    ptr::null_mut(),
69                    ptr::null(),
70                    true,
71                ));
72            } else {
73                obj.set(NewProxyObject(
74                    cx,
75                    handler,
76                    Handle::undefined(),
77                    canonical_proto.get(),
78                    ptr::null(),
79                    false,
80                ));
81            };
82
83            assert!(!obj.is_null());
84            SetProxyReservedSlot(
85                obj.get(),
86                0,
87                &PrivateValue(raw.as_ptr() as *const libc::c_void),
88            );
89        } else {
90            rooted!(&in(cx) let mut proto = ptr::null_mut::<JSObject>());
91            if let Some(given) = given_proto {
92                proto.set(*given);
93                if get_context_realm(cx.raw_cx()) != get_object_realm(*given) {
94                    assert!(JS_WrapObject(cx, proto.handle_mut()));
95                }
96            } else {
97                proto.set(*canonical_proto);
98            }
99            obj.set(JS_NewObjectWithGivenProto(
100                cx,
101                config.class.unwrap(),
102                proto.handle(),
103            ));
104            assert!(!obj.is_null());
105            JS_SetReservedSlot(
106                obj.get(),
107                DOM_OBJECT_SLOT,
108                &PrivateValue(raw.as_ptr() as *const libc::c_void),
109            );
110        };
111
112        let root = raw.reflect_with(obj.get());
113        root.reflector().set_proto_id(config.prototype_id as u16);
114
115        // From here on we copy the legacy unforgeable properties to instance which was originally in a different function.
116        if config.has_legacy_unforgeable_members {
117            rooted!(&in(cx) let mut expando = ptr::null_mut::<JSObject>());
118            if config.is_proxy {
119                ensure_expando_object(cx, obj.handle(), expando.handle_mut());
120            }
121
122            let copy_fn = JS_InitializePropertiesFromCompatibleNativeObject;
123
124            let mut slot = UndefinedValue();
125            JS_GetReservedSlot(
126                canonical_proto.get(),
127                DOM_PROTO_UNFORGEABLE_HOLDER_SLOT,
128                &mut slot,
129            );
130            rooted!(&in(cx) let mut unforgeable_holder = ptr::null_mut::<JSObject>());
131            unforgeable_holder.handle_mut().set(slot.to_object());
132            if config.is_proxy {
133                assert!(copy_fn(cx, expando.handle(), unforgeable_holder.handle()));
134            } else {
135                assert!(copy_fn(cx, obj.handle(), unforgeable_holder.handle()));
136            }
137        }
138
139        DomRoot::from_ref(&*root)
140    }
141}