1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540
|
/* 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/. */
//! Various utilities to glue JavaScript and the DOM implementation together.
use dom::bindings::codegen::InterfaceObjectMap;
use dom::bindings::codegen::PrototypeList;
use dom::bindings::codegen::PrototypeList::{MAX_PROTO_CHAIN_LENGTH, PROTO_OR_IFACE_LENGTH};
use dom::bindings::conversions::{jsstring_to_str, private_from_proto_check};
use dom::bindings::error::throw_invalid_this;
use dom::bindings::inheritance::TopTypeId;
use dom::bindings::str::DOMString;
use dom::bindings::trace::trace_object;
use dom::windowproxy;
use js;
use js::JS_CALLEE;
use js::glue::{CallJitGetterOp, CallJitMethodOp, CallJitSetterOp, IsWrapper};
use js::glue::{GetCrossCompartmentWrapper, WrapperNew};
use js::glue::{RUST_FUNCTION_VALUE_TO_JITINFO, RUST_JSID_IS_INT, RUST_JSID_IS_STRING};
use js::glue::{RUST_JSID_TO_INT, RUST_JSID_TO_STRING, UnwrapObject};
use js::jsapi::{CallArgs, DOMCallbacks, GetGlobalForObjectCrossCompartment};
use js::jsapi::{HandleId, HandleObject, HandleValue, Heap, JSAutoCompartment, JSContext};
use js::jsapi::{JSJitInfo, JSObject, JSTracer, JSWrapObjectCallbacks};
use js::jsapi::{JS_DeletePropertyById, JS_EnumerateStandardClasses};
use js::jsapi::{JS_ForwardGetPropertyTo, JS_GetLatin1StringCharsAndLength};
use js::jsapi::{JS_GetProperty, JS_GetPrototype, JS_GetReservedSlot, JS_HasProperty};
use js::jsapi::{JS_HasPropertyById, JS_IsExceptionPending, JS_IsGlobalObject};
use js::jsapi::{JS_ResolveStandardClass, JS_SetProperty, ToWindowProxyIfWindow};
use js::jsapi::{JS_StringHasLatin1Chars, MutableHandleValue, ObjectOpResult};
use js::jsval::{JSVal, UndefinedValue};
use js::rust::{GCMethods, ToString, get_object_class, is_dom_class};
use libc;
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
use std::ffi::CString;
use std::os::raw::{c_char, c_void};
use std::ptr;
use std::slice;
/// Proxy handler for a WindowProxy.
pub struct WindowProxyHandler(pub *const libc::c_void);
impl MallocSizeOf for WindowProxyHandler {
fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
// FIXME(#6907) this is a pointer to memory allocated by `new` in NewProxyHandler in rust-mozjs.
0
}
}
#[derive(JSTraceable, MallocSizeOf)]
/// Static data associated with a global object.
pub struct GlobalStaticData {
/// The WindowProxy proxy handler for this global.
pub windowproxy_handler: WindowProxyHandler,
}
impl GlobalStaticData {
/// Creates a new GlobalStaticData.
pub fn new() -> GlobalStaticData {
GlobalStaticData {
windowproxy_handler: windowproxy::new_window_proxy_handler(),
}
}
}
/// The index of the slot where the object holder of that interface's
/// unforgeable members are defined.
pub const DOM_PROTO_UNFORGEABLE_HOLDER_SLOT: u32 = 0;
/// The index of the slot that contains a reference to the ProtoOrIfaceArray.
// All DOM globals must have a slot at DOM_PROTOTYPE_SLOT.
pub const DOM_PROTOTYPE_SLOT: u32 = js::JSCLASS_GLOBAL_SLOT_COUNT;
/// The flag set on the `JSClass`es for DOM global objects.
// NOTE: This is baked into the Ion JIT as 0 in codegen for LGetDOMProperty and
// LSetDOMProperty. Those constants need to be changed accordingly if this value
// changes.
pub const JSCLASS_DOM_GLOBAL: u32 = js::JSCLASS_USERBIT1;
/// The struct that holds inheritance information for DOM object reflectors.
#[derive(Clone, Copy)]
pub struct DOMClass {
/// A list of interfaces that this object implements, in order of decreasing
/// derivedness.
pub interface_chain: [PrototypeList::ID; MAX_PROTO_CHAIN_LENGTH],
/// The type ID of that interface.
pub type_id: TopTypeId,
/// The MallocSizeOf function wrapper for that interface.
pub malloc_size_of: unsafe fn(ops: &mut MallocSizeOfOps, *const c_void) -> usize,
/// The `Globals` flag for this global interface, if any.
pub global: InterfaceObjectMap::Globals,
}
unsafe impl Sync for DOMClass {}
/// The JSClass used for DOM object reflectors.
#[derive(Copy)]
pub struct DOMJSClass {
/// The actual JSClass.
pub base: js::jsapi::JSClass,
/// Associated data for DOM object reflectors.
pub dom_class: DOMClass,
}
impl Clone for DOMJSClass {
fn clone(&self) -> DOMJSClass {
*self
}
}
unsafe impl Sync for DOMJSClass {}
/// Returns the ProtoOrIfaceArray for the given global object.
/// Fails if `global` is not a DOM global object.
pub fn get_proto_or_iface_array(global: *mut JSObject) -> *mut ProtoOrIfaceArray {
unsafe {
assert_ne!(((*get_object_class(global)).flags & JSCLASS_DOM_GLOBAL), 0);
JS_GetReservedSlot(global, DOM_PROTOTYPE_SLOT).to_private() as *mut ProtoOrIfaceArray
}
}
/// An array of *mut JSObject of size PROTO_OR_IFACE_LENGTH.
pub type ProtoOrIfaceArray = [*mut JSObject; PROTO_OR_IFACE_LENGTH];
/// Gets the property `id` on `proxy`'s prototype. If it exists, `*found` is
/// set to true and `*vp` to the value, otherwise `*found` is set to false.
///
/// Returns false on JSAPI failure.
pub unsafe fn get_property_on_prototype(cx: *mut JSContext,
proxy: HandleObject,
receiver: HandleValue,
id: HandleId,
found: *mut bool,
vp: MutableHandleValue)
-> bool {
rooted!(in(cx) let mut proto = ptr::null_mut::<JSObject>());
if !JS_GetPrototype(cx, proxy, proto.handle_mut()) || proto.is_null() {
*found = false;
return true;
}
let mut has_property = false;
if !JS_HasPropertyById(cx, proto.handle(), id, &mut has_property) {
return false;
}
*found = has_property;
let no_output = vp.ptr.is_null();
if !has_property || no_output {
return true;
}
JS_ForwardGetPropertyTo(cx, proto.handle(), id, receiver, vp)
}
/// Get an array index from the given `jsid`. Returns `None` if the given
/// `jsid` is not an integer.
pub fn get_array_index_from_id(_cx: *mut JSContext, id: HandleId) -> Option<u32> {
unsafe {
if RUST_JSID_IS_INT(id) {
return Some(RUST_JSID_TO_INT(id) as u32);
}
None
}
// if id is length atom, -1, otherwise
/*return if JSID_IS_ATOM(id) {
let atom = JSID_TO_ATOM(id);
//let s = *GetAtomChars(id);
if s > 'a' && s < 'z' {
return -1;
}
let i = 0;
let str = AtomToLinearString(JSID_TO_ATOM(id));
return if StringIsArray(str, &mut i) != 0 { i } else { -1 }
} else {
IdToInt32(cx, id);
}*/
}
/// Find the enum equivelent of a string given by `v` in `pairs`.
/// Returns `Err(())` on JSAPI failure (there is a pending exception), and
/// `Ok((None, value))` if there was no matching string.
pub unsafe fn find_enum_value<'a, T>(cx: *mut JSContext,
v: HandleValue,
pairs: &'a [(&'static str, T)])
-> Result<(Option<&'a T>, DOMString), ()> {
let jsstr = ToString(cx, v);
if jsstr.is_null() {
return Err(());
}
let search = jsstring_to_str(cx, jsstr);
Ok((pairs.iter().find(|&&(key, _)| search == *key).map(|&(_, ref ev)| ev), search))
}
/// Returns wether `obj` is a platform object
/// <https://heycam.github.io/webidl/#dfn-platform-object>
pub fn is_platform_object(obj: *mut JSObject) -> bool {
unsafe {
// Fast-path the common case
let mut clasp = get_object_class(obj);
if is_dom_class(&*clasp) {
return true;
}
// Now for simplicity check for security wrappers before anything else
if IsWrapper(obj) {
let unwrapped_obj = UnwrapObject(obj, /* stopAtWindowProxy = */ 0);
if unwrapped_obj.is_null() {
return false;
}
clasp = get_object_class(obj);
}
// TODO also check if JS_IsArrayBufferObject
is_dom_class(&*clasp)
}
}
/// Get the property with name `property` from `object`.
/// Returns `Err(())` on JSAPI failure (there is a pending exception), and
/// `Ok(false)` if there was no property with the given name.
pub fn get_dictionary_property(cx: *mut JSContext,
object: HandleObject,
property: &str,
rval: MutableHandleValue)
-> Result<bool, ()> {
fn has_property(cx: *mut JSContext,
object: HandleObject,
property: &CString,
found: &mut bool)
-> bool {
unsafe { JS_HasProperty(cx, object, property.as_ptr(), found) }
}
fn get_property(cx: *mut JSContext,
object: HandleObject,
property: &CString,
value: MutableHandleValue)
-> bool {
unsafe { JS_GetProperty(cx, object, property.as_ptr(), value) }
}
let property = CString::new(property).unwrap();
if object.get().is_null() {
return Ok(false);
}
let mut found = false;
if !has_property(cx, object, &property, &mut found) {
return Err(());
}
if !found {
return Ok(false);
}
if !get_property(cx, object, &property, rval) {
return Err(());
}
Ok(true)
}
/// Set the property with name `property` from `object`.
/// Returns `Err(())` on JSAPI failure, or null object,
/// and Ok(()) otherwise
pub fn set_dictionary_property(cx: *mut JSContext,
object: HandleObject,
property: &str,
value: HandleValue)
-> Result<(), ()> {
if object.get().is_null() {
return Err(());
}
let property = CString::new(property).unwrap();
unsafe {
if !JS_SetProperty(cx, object, property.as_ptr(), value) {
return Err(());
}
}
Ok(())
}
/// Returns whether `proxy` has a property `id` on its prototype.
pub unsafe fn has_property_on_prototype(cx: *mut JSContext,
proxy: HandleObject,
id: HandleId,
found: &mut bool)
-> bool {
rooted!(in(cx) let mut proto = ptr::null_mut::<JSObject>());
if !JS_GetPrototype(cx, proxy, proto.handle_mut()) {
return false;
}
assert!(!proto.is_null());
JS_HasPropertyById(cx, proto.handle(), id, found)
}
/// Drop the resources held by reserved slots of a global object
pub unsafe fn finalize_global(obj: *mut JSObject) {
let protolist = get_proto_or_iface_array(obj);
let list = (*protolist).as_mut_ptr();
for idx in 0..PROTO_OR_IFACE_LENGTH as isize {
let entry = list.offset(idx);
let value = *entry;
<*mut JSObject>::post_barrier(entry, value, ptr::null_mut());
}
let _: Box<ProtoOrIfaceArray> = Box::from_raw(protolist);
}
/// Trace the resources held by reserved slots of a global object
pub unsafe fn trace_global(tracer: *mut JSTracer, obj: *mut JSObject) {
let array = get_proto_or_iface_array(obj);
for proto in (*array).iter() {
if !proto.is_null() {
trace_object(tracer,
"prototype",
&*(proto as *const *mut JSObject as *const Heap<*mut JSObject>));
}
}
}
/// Enumerate lazy properties of a global object.
pub unsafe extern "C" fn enumerate_global(cx: *mut JSContext, obj: HandleObject) -> bool {
assert!(JS_IsGlobalObject(obj.get()));
if !JS_EnumerateStandardClasses(cx, obj) {
return false;
}
for init_fun in InterfaceObjectMap::MAP.values() {
init_fun(cx, obj);
}
true
}
/// Resolve a lazy global property, for interface objects and named constructors.
pub unsafe extern "C" fn resolve_global(
cx: *mut JSContext,
obj: HandleObject,
id: HandleId,
rval: *mut bool)
-> bool {
assert!(JS_IsGlobalObject(obj.get()));
if !JS_ResolveStandardClass(cx, obj, id, rval) {
return false;
}
if *rval {
return true;
}
if !RUST_JSID_IS_STRING(id) {
*rval = false;
return true;
}
let string = RUST_JSID_TO_STRING(id);
if !JS_StringHasLatin1Chars(string) {
*rval = false;
return true;
}
let mut length = 0;
let ptr = JS_GetLatin1StringCharsAndLength(cx, ptr::null(), string, &mut length);
assert!(!ptr.is_null());
let bytes = slice::from_raw_parts(ptr, length as usize);
if let Some(init_fun) = InterfaceObjectMap::MAP.get(bytes) {
init_fun(cx, obj);
*rval = true;
} else {
*rval = false;
}
true
}
unsafe extern "C" fn wrap(cx: *mut JSContext,
_existing: HandleObject,
obj: HandleObject)
-> *mut JSObject {
// FIXME terrible idea. need security wrappers
// https://github.com/servo/servo/issues/2382
WrapperNew(cx, obj, GetCrossCompartmentWrapper(), ptr::null(), false)
}
unsafe extern "C" fn pre_wrap(cx: *mut JSContext,
_existing: HandleObject,
obj: HandleObject,
_object_passed_to_wrap: HandleObject)
-> *mut JSObject {
let _ac = JSAutoCompartment::new(cx, obj.get());
let obj = ToWindowProxyIfWindow(obj.get());
assert!(!obj.is_null());
obj
}
/// Callback table for use with JS_SetWrapObjectCallbacks
pub static WRAP_CALLBACKS: JSWrapObjectCallbacks = JSWrapObjectCallbacks {
wrap: Some(wrap),
preWrap: Some(pre_wrap),
};
/// Deletes the property `id` from `object`.
pub unsafe fn delete_property_by_id(cx: *mut JSContext,
object: HandleObject,
id: HandleId,
bp: *mut ObjectOpResult)
-> bool {
JS_DeletePropertyById(cx, object, id, bp)
}
unsafe fn generic_call(cx: *mut JSContext,
argc: libc::c_uint,
vp: *mut JSVal,
is_lenient: bool,
call: unsafe extern fn(*const JSJitInfo, *mut JSContext,
HandleObject, *mut libc::c_void, u32,
*mut JSVal)
-> bool)
-> bool {
let args = CallArgs::from_vp(vp, argc);
let info = RUST_FUNCTION_VALUE_TO_JITINFO(JS_CALLEE(cx, vp));
let proto_id = (*info).protoID;
let thisobj = args.thisv();
if !thisobj.get().is_null_or_undefined() && !thisobj.get().is_object() {
throw_invalid_this(cx, proto_id);
return false;
}
let obj = if thisobj.get().is_object() {
thisobj.get().to_object()
} else {
GetGlobalForObjectCrossCompartment(JS_CALLEE(cx, vp).to_object_or_null())
};
rooted!(in(cx) let obj = obj);
let depth = (*info).depth;
let proto_check = |class: &'static DOMClass| {
class.interface_chain[depth as usize] as u16 == proto_id
};
let this = match private_from_proto_check(obj.get(), proto_check) {
Ok(val) => val,
Err(()) => {
if is_lenient {
debug_assert!(!JS_IsExceptionPending(cx));
*vp = UndefinedValue();
return true;
} else {
throw_invalid_this(cx, proto_id);
return false;
}
}
};
call(info, cx, obj.handle(), this as *mut libc::c_void, argc, vp)
}
/// Generic method of IDL interface.
pub unsafe extern "C" fn generic_method(cx: *mut JSContext,
argc: libc::c_uint,
vp: *mut JSVal)
-> bool {
generic_call(cx, argc, vp, false, CallJitMethodOp)
}
/// Generic getter of IDL interface.
pub unsafe extern "C" fn generic_getter(cx: *mut JSContext,
argc: libc::c_uint,
vp: *mut JSVal)
-> bool {
generic_call(cx, argc, vp, false, CallJitGetterOp)
}
/// Generic lenient getter of IDL interface.
pub unsafe extern "C" fn generic_lenient_getter(cx: *mut JSContext,
argc: libc::c_uint,
vp: *mut JSVal)
-> bool {
generic_call(cx, argc, vp, true, CallJitGetterOp)
}
unsafe extern "C" fn call_setter(info: *const JSJitInfo,
cx: *mut JSContext,
handle: HandleObject,
this: *mut libc::c_void,
argc: u32,
vp: *mut JSVal)
-> bool {
if !CallJitSetterOp(info, cx, handle, this, argc, vp) {
return false;
}
*vp = UndefinedValue();
true
}
/// Generic setter of IDL interface.
pub unsafe extern "C" fn generic_setter(cx: *mut JSContext,
argc: libc::c_uint,
vp: *mut JSVal)
-> bool {
generic_call(cx, argc, vp, false, call_setter)
}
/// Generic lenient setter of IDL interface.
pub unsafe extern "C" fn generic_lenient_setter(cx: *mut JSContext,
argc: libc::c_uint,
vp: *mut JSVal)
-> bool {
generic_call(cx, argc, vp, true, call_setter)
}
unsafe extern "C" fn instance_class_has_proto_at_depth(clasp: *const js::jsapi::Class,
proto_id: u32,
depth: u32)
-> bool {
let domclass: *const DOMJSClass = clasp as *const _;
let domclass = &*domclass;
domclass.dom_class.interface_chain[depth as usize] as u32 == proto_id
}
#[allow(missing_docs)] // FIXME
pub const DOM_CALLBACKS: DOMCallbacks = DOMCallbacks {
instanceClassMatchesProto: Some(instance_class_has_proto_at_depth),
};
// Generic method for returning libc::c_void from caller
pub trait AsVoidPtr {
fn as_void_ptr(&self) -> *const libc::c_void;
}
impl<T> AsVoidPtr for T {
fn as_void_ptr(&self) -> *const libc::c_void {
self as *const T as *const libc::c_void
}
}
// Generic method for returning c_char from caller
pub trait AsCCharPtrPtr {
fn as_c_char_ptr(&self) -> *const c_char;
}
impl AsCCharPtrPtr for [u8] {
fn as_c_char_ptr(&self) -> *const c_char {
self as *const [u8] as *const c_char
}
}
|