/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods; use crate::dom::bindings::proxyhandler::set_property_descriptor; use crate::dom::bindings::root::Root; use crate::dom::bindings::utils::has_property_on_prototype; use crate::dom::globalscope::GlobalScope; use crate::dom::window::Window; use crate::js::conversions::ToJSValConvertible; use crate::script_runtime::JSContext as SafeJSContext; use js::conversions::jsstr_to_string; use js::error::throw_type_error; use js::glue::RUST_JSID_TO_STRING; use js::glue::{CreateProxyHandler, NewProxyObject, ProxyTraps, RUST_JSID_IS_STRING}; use js::jsapi::JS_SetImmutablePrototype; use js::jsapi::{ Handle, HandleObject, JSClass, JSContext, JSErrNum, MutableHandleObject, UndefinedHandleValue, }; use js::jsapi::{ HandleId, JSClass_NON_NATIVE, MutableHandle, MutableHandleIdVector, ObjectOpResult, PropertyDescriptor, ProxyClassExtension, ProxyClassOps, ProxyObjectOps, JSCLASS_DELAY_METADATA_BUILDER, JSCLASS_IS_PROXY, JSCLASS_RESERVED_SLOTS_MASK, JSCLASS_RESERVED_SLOTS_SHIFT, }; use js::jsval::UndefinedValue; use js::rust::IntoHandle; use js::rust::{Handle as RustHandle, MutableHandle as RustMutableHandle}; use js::rust::{HandleObject as RustHandleObject, MutableHandleObject as RustMutableHandleObject}; use libc; use std::ptr; struct SyncWrapper(*const libc::c_void); #[allow(unsafe_code)] unsafe impl Sync for SyncWrapper {} lazy_static! { static ref HANDLER: SyncWrapper = { let traps = ProxyTraps { enter: None, getOwnPropertyDescriptor: Some(get_own_property_descriptor), defineProperty: Some(define_property), ownPropertyKeys: Some(own_property_keys), delete_: Some(delete), enumerate: None, getPrototypeIfOrdinary: Some(get_prototype_if_ordinary), getPrototype: None, setPrototype: None, setImmutablePrototype: None, preventExtensions: Some(prevent_extensions), isExtensible: Some(is_extensible), has: None, get: None, set: None, call: None, construct: None, hasOwn: None, getOwnEnumerablePropertyKeys: None, nativeCall: None, objectClassIs: None, className: Some(class_name), fun_toString: None, boxedValue_unbox: None, defaultValue: None, trace: None, finalize: None, objectMoved: None, isCallable: None, isConstructor: None, }; #[allow(unsafe_code)] unsafe { SyncWrapper(CreateProxyHandler(&traps, ptr::null())) } }; } #[allow(unsafe_code)] unsafe extern "C" fn get_own_property_descriptor( cx: *mut JSContext, proxy: HandleObject, id: HandleId, desc: MutableHandle, is_none: *mut bool, ) -> bool { let cx = SafeJSContext::from_ptr(cx); if !RUST_JSID_IS_STRING(id) { // Nothing to do if we're resolving a non-string property. return true; } let mut found = false; if !has_property_on_prototype( *cx, RustHandle::from_raw(proxy), RustHandle::from_raw(id), &mut found, ) { return false; } if found { return true; } let s = jsstr_to_string(*cx, RUST_JSID_TO_STRING(id)); if s.is_empty() { return true; } let window = Root::downcast::(GlobalScope::from_object(proxy.get())) .expect("global is not a window"); if let Some(obj) = window.NamedGetter(cx, s.into()) { rooted!(in(*cx) let mut rval = UndefinedValue()); obj.to_jsval(*cx, rval.handle_mut()); set_property_descriptor(RustMutableHandle::from_raw(desc), rval.handle(), 0, &mut *is_none); } return true; } #[allow(unsafe_code)] unsafe extern "C" fn own_property_keys( _cx: *mut JSContext, _proxy: HandleObject, _props: MutableHandleIdVector, ) -> bool { // FIXME(pylbrecht): dummy implementation true } #[allow(unsafe_code)] unsafe extern "C" fn define_property( cx: *mut JSContext, _proxy: HandleObject, _id: HandleId, _desc: Handle, _result: *mut ObjectOpResult, ) -> bool { throw_type_error( cx, "Not allowed to define a property on the named properties object.", ); false } #[allow(unsafe_code)] unsafe extern "C" fn delete( _cx: *mut JSContext, _proxy: HandleObject, _id: HandleId, result: *mut ObjectOpResult, ) -> bool { (*result).code_ = JSErrNum::JSMSG_CANT_DELETE_WINDOW_NAMED_PROPERTY as usize; true } #[allow(unsafe_code)] unsafe extern "C" fn get_prototype_if_ordinary( _cx: *mut JSContext, proxy: HandleObject, is_ordinary: *mut bool, proto: MutableHandleObject, ) -> bool { *is_ordinary = true; proto.set(js::jsapi::GetStaticPrototype(proxy.get())); true } #[allow(unsafe_code)] unsafe extern "C" fn prevent_extensions( _cx: *mut JSContext, _proxy: HandleObject, result: *mut ObjectOpResult, ) -> bool { (*result).code_ = JSErrNum::JSMSG_CANT_PREVENT_EXTENSIONS as usize; true } #[allow(unsafe_code)] unsafe extern "C" fn is_extensible( _cx: *mut JSContext, _proxy: HandleObject, extensible: *mut bool, ) -> bool { *extensible = true; true } #[allow(unsafe_code)] unsafe extern "C" fn class_name(_cx: *mut JSContext, _proxy: HandleObject) -> *const i8 { &b"WindowProperties\0" as *const _ as *const i8 } // Maybe this should be a DOMJSClass. See https://bugzilla.mozilla.org/show_bug.cgi?id=787070 #[allow(unsafe_code)] static CLASS: JSClass = JSClass { name: b"WindowProperties\0" as *const u8 as *const libc::c_char, flags: JSClass_NON_NATIVE | JSCLASS_IS_PROXY | JSCLASS_DELAY_METADATA_BUILDER | ((1 & JSCLASS_RESERVED_SLOTS_MASK) << JSCLASS_RESERVED_SLOTS_SHIFT), /* JSCLASS_HAS_RESERVED_SLOTS(1) */ cOps: unsafe { &ProxyClassOps }, spec: ptr::null(), ext: unsafe { &ProxyClassExtension }, oOps: unsafe { &ProxyObjectOps }, }; #[allow(unsafe_code)] pub fn create( cx: SafeJSContext, proto: RustHandleObject, mut properties_obj: RustMutableHandleObject, ) { unsafe { properties_obj.set(NewProxyObject( *cx, HANDLER.0, UndefinedHandleValue, proto.get(), // TODO: pass proper clasp &CLASS, false, )); assert!(!properties_obj.get().is_null()); let mut succeeded = false; assert!(JS_SetImmutablePrototype( *cx, properties_obj.handle().into_handle(), &mut succeeded )); assert!(succeeded); } }