|
| 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 http://mozilla.org/MPL/2.0/. */ |
| 4 | + |
| 5 | +//! Machinery to initialise interface prototype objects and interface objects. |
| 6 | +
|
| 7 | +use dom::bindings::codegen::PrototypeList; |
| 8 | +use dom::bindings::conversions::get_dom_class; |
| 9 | +use dom::bindings::utils::{ConstantSpec, NonNullJSNative, define_constants}; |
| 10 | +use js::jsapi::{HandleObject, HandleValue, JSClass, JSContext, JSFunctionSpec}; |
| 11 | +use js::jsapi::{JSPropertySpec, JS_DefineProperty1, JS_DefineProperty2}; |
| 12 | +use js::jsapi::{JS_DefineProperty4, JS_GetFunctionObject, JS_GetPrototype}; |
| 13 | +use js::jsapi::{JS_InternString, JS_LinkConstructorAndPrototype, JS_NewFunction}; |
| 14 | +use js::jsapi::{JS_NewObject, JS_NewObjectWithUniqueType, MutableHandleObject}; |
| 15 | +use js::jsapi::{RootedObject, RootedString}; |
| 16 | +use js::rust::{define_methods, define_properties}; |
| 17 | +use js::{JSFUN_CONSTRUCTOR, JSPROP_PERMANENT, JSPROP_READONLY}; |
| 18 | +use libc; |
| 19 | +use std::ptr; |
| 20 | + |
| 21 | +/// Create and define the interface object of a callback interface. |
| 22 | +pub unsafe fn create_callback_interface_object(cx: *mut JSContext, |
| 23 | + receiver: HandleObject, |
| 24 | + constants: &'static [ConstantSpec], |
| 25 | + name: &'static [u8]) { |
| 26 | + assert!(!constants.is_empty()); |
| 27 | + let obj = RootedObject::new(cx, JS_NewObject(cx, ptr::null())); |
| 28 | + assert!(!obj.ptr.is_null()); |
| 29 | + define_constants(cx, obj.handle(), constants); |
| 30 | + define_name(cx, obj.handle(), name); |
| 31 | + define_global_object(cx, receiver, name, obj.handle()); |
| 32 | +} |
| 33 | + |
| 34 | +/// Create the interface prototype object of a non-callback interface. |
| 35 | +pub unsafe fn create_interface_prototype_object( |
| 36 | + cx: *mut JSContext, |
| 37 | + proto: HandleObject, |
| 38 | + class: &'static JSClass, |
| 39 | + regular_methods: Option<&'static [JSFunctionSpec]>, |
| 40 | + regular_properties: Option<&'static [JSPropertySpec]>, |
| 41 | + constants: &'static [ConstantSpec], |
| 42 | + rval: MutableHandleObject) { |
| 43 | + create_object(cx, proto, class, regular_methods, regular_properties, constants, rval); |
| 44 | +} |
| 45 | + |
| 46 | +/// Create and define the interface object of a non-callback interface. |
| 47 | +pub unsafe fn create_noncallback_interface_object( |
| 48 | + cx: *mut JSContext, |
| 49 | + receiver: HandleObject, |
| 50 | + proto: HandleObject, |
| 51 | + class: &'static JSClass, |
| 52 | + static_methods: Option<&'static [JSFunctionSpec]>, |
| 53 | + static_properties: Option<&'static [JSPropertySpec]>, |
| 54 | + constants: &'static [ConstantSpec], |
| 55 | + interface_prototype_object: HandleObject, |
| 56 | + name: &'static [u8], |
| 57 | + length: u32, |
| 58 | + rval: MutableHandleObject) { |
| 59 | + create_object(cx, proto, class, static_methods, static_properties, constants, rval); |
| 60 | + assert!(JS_LinkConstructorAndPrototype(cx, rval.handle(), interface_prototype_object)); |
| 61 | + define_name(cx, rval.handle(), name); |
| 62 | + define_length(cx, rval.handle(), length); |
| 63 | + define_global_object(cx, receiver, name, rval.handle()); |
| 64 | +} |
| 65 | + |
| 66 | +/// Create and define the named constructors of a non-callback interface. |
| 67 | +pub unsafe fn create_named_constructors( |
| 68 | + cx: *mut JSContext, |
| 69 | + receiver: HandleObject, |
| 70 | + named_constructors: &[(NonNullJSNative, &'static [u8], u32)], |
| 71 | + interface_prototype_object: HandleObject) { |
| 72 | + let mut constructor = RootedObject::new(cx, ptr::null_mut()); |
| 73 | + |
| 74 | + for &(native, name, arity) in named_constructors { |
| 75 | + assert!(*name.last().unwrap() == b'\0'); |
| 76 | + |
| 77 | + let fun = JS_NewFunction(cx, |
| 78 | + Some(native), |
| 79 | + arity, |
| 80 | + JSFUN_CONSTRUCTOR, |
| 81 | + name.as_ptr() as *const libc::c_char); |
| 82 | + assert!(!fun.is_null()); |
| 83 | + constructor.ptr = JS_GetFunctionObject(fun); |
| 84 | + assert!(!constructor.ptr.is_null()); |
| 85 | + |
| 86 | + assert!(JS_DefineProperty1(cx, |
| 87 | + constructor.handle(), |
| 88 | + b"prototype\0".as_ptr() as *const libc::c_char, |
| 89 | + interface_prototype_object, |
| 90 | + JSPROP_PERMANENT | JSPROP_READONLY, |
| 91 | + None, |
| 92 | + None)); |
| 93 | + |
| 94 | + define_global_object(cx, receiver, name, constructor.handle()); |
| 95 | + } |
| 96 | +} |
| 97 | + |
| 98 | +/// Return whether a value is an instance of a given prototype. |
| 99 | +/// http://heycam.github.io/webidl/#es-interface-hasinstance |
| 100 | +pub unsafe fn has_instance( |
| 101 | + cx: *mut JSContext, |
| 102 | + prototype: HandleObject, |
| 103 | + value: HandleValue, |
| 104 | + id: PrototypeList::ID, |
| 105 | + index: usize) |
| 106 | + -> bool { |
| 107 | + if !value.is_object() { |
| 108 | + // Step 1. |
| 109 | + return false; |
| 110 | + } |
| 111 | + let mut value = RootedObject::new(cx, value.to_object()); |
| 112 | + |
| 113 | + // Steps 2-3 only concern callback interface objects. |
| 114 | + |
| 115 | + if let Ok(dom_class) = get_dom_class(value.ptr) { |
| 116 | + if dom_class.interface_chain[index] == id { |
| 117 | + // Step 4. |
| 118 | + return true; |
| 119 | + } |
| 120 | + } |
| 121 | + |
| 122 | + while JS_GetPrototype(cx, value.handle(), value.handle_mut()) { |
| 123 | + if value.ptr as *const _ == prototype.ptr { |
| 124 | + // Step 5.3. |
| 125 | + return true; |
| 126 | + } |
| 127 | + } |
| 128 | + // Step 5.2. |
| 129 | + false |
| 130 | +} |
| 131 | + |
| 132 | +unsafe fn create_object( |
| 133 | + cx: *mut JSContext, |
| 134 | + proto: HandleObject, |
| 135 | + class: &'static JSClass, |
| 136 | + methods: Option<&'static [JSFunctionSpec]>, |
| 137 | + properties: Option<&'static [JSPropertySpec]>, |
| 138 | + constants: &'static [ConstantSpec], |
| 139 | + rval: MutableHandleObject) { |
| 140 | + rval.set(JS_NewObjectWithUniqueType(cx, class, proto)); |
| 141 | + assert!(!rval.ptr.is_null()); |
| 142 | + if let Some(methods) = methods { |
| 143 | + define_methods(cx, rval.handle(), methods).unwrap(); |
| 144 | + } |
| 145 | + if let Some(properties) = properties { |
| 146 | + define_properties(cx, rval.handle(), properties).unwrap(); |
| 147 | + } |
| 148 | + define_constants(cx, rval.handle(), constants); |
| 149 | +} |
| 150 | + |
| 151 | +unsafe fn define_name(cx: *mut JSContext, obj: HandleObject, name: &'static [u8]) { |
| 152 | + assert!(*name.last().unwrap() == b'\0'); |
| 153 | + let name = |
| 154 | + RootedString::new(cx, JS_InternString(cx, name.as_ptr() as *const libc::c_char)); |
| 155 | + assert!(!name.ptr.is_null()); |
| 156 | + assert!(JS_DefineProperty2(cx, |
| 157 | + obj, |
| 158 | + b"name\0".as_ptr() as *const libc::c_char, |
| 159 | + name.handle(), |
| 160 | + JSPROP_READONLY, |
| 161 | + None, None)); |
| 162 | +} |
| 163 | + |
| 164 | +unsafe fn define_length(cx: *mut JSContext, obj: HandleObject, length: u32) { |
| 165 | + assert!(JS_DefineProperty4(cx, |
| 166 | + obj, |
| 167 | + b"length\0".as_ptr() as *const libc::c_char, |
| 168 | + length, |
| 169 | + JSPROP_READONLY, |
| 170 | + None, None)); |
| 171 | +} |
| 172 | + |
| 173 | +unsafe fn define_global_object( |
| 174 | + cx: *mut JSContext, |
| 175 | + receiver: HandleObject, |
| 176 | + name: &'static [u8], |
| 177 | + obj: HandleObject) { |
| 178 | + assert!(JS_DefineProperty1(cx, |
| 179 | + receiver, |
| 180 | + name.as_ptr() as *const libc::c_char, |
| 181 | + obj, |
| 182 | + 0, |
| 183 | + None, None)); |
| 184 | +} |
0 commit comments