mirror of
https://github.com/servo/servo.git
synced 2025-07-16 03:43:38 +01:00
308 lines
12 KiB
Rust
308 lines
12 KiB
Rust
/* 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 dom::bindings::node::unwrap;
|
|
use dom::bindings::utils::jsval_to_str;
|
|
use dom::bindings::utils::{domstring_to_jsval, WrapNewBindingObject};
|
|
use dom::bindings::utils::{str, CacheableWrapper, DOM_OBJECT_SLOT, DOMString};
|
|
use dom::element::Element;
|
|
use dom::element::{HTMLImageElementTypeId, HTMLHeadElementTypeId, HTMLScriptElementTypeId,
|
|
HTMLDivElementTypeId};
|
|
use dom::node::{AbstractNode, ScriptView, ElementNodeTypeId};
|
|
use layout_interface::{ContentBoxQuery, ContentBoxResponse};
|
|
use script_task::page_from_context;
|
|
use super::utils;
|
|
|
|
use std::cast;
|
|
use std::i32;
|
|
use std::libc;
|
|
use std::libc::c_uint;
|
|
use std::comm;
|
|
use std::ptr;
|
|
use std::ptr::null;
|
|
use js::glue::*;
|
|
use js::jsapi::*;
|
|
use js::jsapi::{JSContext, JSVal, JSObject, JSBool, JSFreeOp, JSPropertySpec};
|
|
use js::jsapi::{JSNativeWrapper, JSTracer, JSTRACE_OBJECT};
|
|
use js::jsapi::{JSPropertyOpWrapper, JSStrictPropertyOpWrapper, JSFunctionSpec};
|
|
use js::rust::{Compartment, jsobj};
|
|
use js::{JS_ARGV, JSPROP_ENUMERATE, JSPROP_SHARED};
|
|
use js::{JS_THIS_OBJECT, JSPROP_NATIVE_ACCESSORS};
|
|
|
|
extern fn finalize(_fop: *JSFreeOp, obj: *JSObject) {
|
|
debug!("element finalize: %x!", obj as uint);
|
|
unsafe {
|
|
let node: AbstractNode<ScriptView> = unwrap(obj);
|
|
//XXXjdm We need separate finalizers for each specialty element type like headings
|
|
let _elem: @Element = cast::transmute(node.raw_object());
|
|
}
|
|
}
|
|
|
|
pub extern fn trace(tracer: *mut JSTracer, obj: *JSObject) {
|
|
let node = unsafe { unwrap(obj) };
|
|
|
|
fn trace_node(tracer: *mut JSTracer, node: Option<AbstractNode<ScriptView>>, name: &str) {
|
|
if node.is_none() {
|
|
return;
|
|
}
|
|
error!("tracing %s", name);
|
|
let mut node = node.unwrap();
|
|
let cache = node.get_wrappercache();
|
|
let wrapper = cache.get_wrapper();
|
|
assert!(wrapper.is_not_null());
|
|
unsafe {
|
|
(*tracer).debugPrinter = ptr::null();
|
|
(*tracer).debugPrintIndex = -1;
|
|
do name.to_c_str().with_ref |name| {
|
|
(*tracer).debugPrintArg = name as *libc::c_void;
|
|
JS_CallTracer(cast::transmute(tracer), wrapper, JSTRACE_OBJECT as u32);
|
|
}
|
|
}
|
|
}
|
|
error!("tracing %?:", obj as uint);
|
|
trace_node(tracer, node.parent_node(), "parent");
|
|
trace_node(tracer, node.first_child(), "first child");
|
|
trace_node(tracer, node.last_child(), "last child");
|
|
trace_node(tracer, node.next_sibling(), "next sibling");
|
|
trace_node(tracer, node.prev_sibling(), "prev sibling");
|
|
}
|
|
|
|
pub fn init(compartment: @mut Compartment) {
|
|
let obj = utils::define_empty_prototype(~"Element", Some(~"Node"), compartment);
|
|
let attrs = @~[
|
|
JSPropertySpec {
|
|
name: compartment.add_name(~"tagName"),
|
|
tinyid: 0,
|
|
flags: (JSPROP_ENUMERATE | JSPROP_SHARED | JSPROP_NATIVE_ACCESSORS) as u8,
|
|
getter: JSPropertyOpWrapper {op: getTagName, info: null()},
|
|
setter: JSStrictPropertyOpWrapper {op: null(), info: null()}},
|
|
JSPropertySpec {
|
|
name: null(),
|
|
tinyid: 0,
|
|
flags: (JSPROP_SHARED | JSPROP_ENUMERATE | JSPROP_NATIVE_ACCESSORS) as u8,
|
|
getter: JSPropertyOpWrapper {op: null(), info: null()},
|
|
setter: JSStrictPropertyOpWrapper {op: null(), info: null()}}];
|
|
compartment.global_props.push(attrs);
|
|
do attrs.as_imm_buf |specs, _len| {
|
|
unsafe {
|
|
JS_DefineProperties(compartment.cx.ptr, obj.ptr, specs);
|
|
}
|
|
}
|
|
|
|
let methods = @~[JSFunctionSpec {name: compartment.add_name(~"getClientRects"),
|
|
call: JSNativeWrapper {op: getClientRects, info: null()},
|
|
nargs: 0,
|
|
flags: 0,
|
|
selfHostedName: null()},
|
|
JSFunctionSpec {name: compartment.add_name(~"getBoundingClientRect"),
|
|
call: JSNativeWrapper {op: getBoundingClientRect, info: null()},
|
|
nargs: 0,
|
|
flags: 0,
|
|
selfHostedName: null()},
|
|
JSFunctionSpec {name: compartment.add_name(~"setAttribute"),
|
|
call: JSNativeWrapper {op: setAttribute, info: null()},
|
|
nargs: 0,
|
|
flags: 0,
|
|
selfHostedName: null()},
|
|
JSFunctionSpec {name: null(),
|
|
call: JSNativeWrapper {op: null(), info: null()},
|
|
nargs: 0,
|
|
flags: 0,
|
|
selfHostedName: null()}];
|
|
do methods.as_imm_buf |fns, _len| {
|
|
unsafe {
|
|
JS_DefineFunctions(compartment.cx.ptr, obj.ptr, fns);
|
|
}
|
|
}
|
|
|
|
compartment.register_class(utils::instance_jsclass(~"GenericElementInstance",
|
|
finalize, trace));
|
|
|
|
let _ = utils::define_empty_prototype(~"HTMLElement", Some(~"Element"), compartment);
|
|
let _ = utils::define_empty_prototype(~"HTMLDivElement", Some(~"HTMLElement"), compartment);
|
|
let _ = utils::define_empty_prototype(~"HTMLScriptElement", Some(~"HTMLElement"), compartment);
|
|
let _ = utils::define_empty_prototype(~"HTMLHeadElement", Some(~"HTMLElement"), compartment);
|
|
|
|
let obj = utils::define_empty_prototype(~"HTMLImageElement", Some(~"HTMLElement"), compartment);
|
|
let attrs = @~[
|
|
JSPropertySpec {name: compartment.add_name(~"width"),
|
|
tinyid: 0,
|
|
flags: (JSPROP_SHARED | JSPROP_ENUMERATE | JSPROP_NATIVE_ACCESSORS) as u8,
|
|
getter: JSPropertyOpWrapper {op: HTMLImageElement_getWidth, info: null()},
|
|
setter: JSStrictPropertyOpWrapper {op: HTMLImageElement_setWidth, info: null()}},
|
|
JSPropertySpec {name: null(),
|
|
tinyid: 0,
|
|
flags: (JSPROP_SHARED | JSPROP_ENUMERATE | JSPROP_NATIVE_ACCESSORS) as u8,
|
|
getter: JSPropertyOpWrapper {op: null(), info: null()},
|
|
setter: JSStrictPropertyOpWrapper {op: null(), info: null()}}];
|
|
compartment.global_props.push(attrs);
|
|
do attrs.as_imm_buf |specs, _len| {
|
|
unsafe {
|
|
JS_DefineProperties(compartment.cx.ptr, obj.ptr, specs);
|
|
}
|
|
}
|
|
}
|
|
|
|
extern fn getClientRects(cx: *JSContext, _argc: c_uint, vp: *JSVal) -> JSBool {
|
|
unsafe {
|
|
let obj = JS_THIS_OBJECT(cx, vp);
|
|
let mut node = unwrap(obj);
|
|
let rval = do node.with_imm_element |elem| {
|
|
elem.GetClientRects(node)
|
|
};
|
|
let cache = node.get_wrappercache();
|
|
let rval = rval as @mut CacheableWrapper;
|
|
assert!(WrapNewBindingObject(cx, cache.get_wrapper(),
|
|
rval,
|
|
cast::transmute(vp)));
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
extern fn getBoundingClientRect(cx: *JSContext, _argc: c_uint, vp: *JSVal) -> JSBool {
|
|
unsafe {
|
|
let obj = JS_THIS_OBJECT(cx, vp);
|
|
let mut node = unwrap(obj);
|
|
let rval = do node.with_imm_element |elem| {
|
|
elem.GetBoundingClientRect(node)
|
|
};
|
|
let cache = node.get_wrappercache();
|
|
let rval = rval as @mut CacheableWrapper;
|
|
assert!(WrapNewBindingObject(cx, cache.get_wrapper(),
|
|
rval,
|
|
cast::transmute(vp)));
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
extern fn setAttribute(cx: *JSContext, argc: c_uint, vp: *JSVal) -> JSBool {
|
|
unsafe {
|
|
let obj = JS_THIS_OBJECT(cx, vp);
|
|
let node = unwrap(obj);
|
|
|
|
if (argc < 2) {
|
|
return 0; //XXXjdm throw exception
|
|
}
|
|
|
|
let argv = JS_ARGV(cx, cast::transmute(vp));
|
|
|
|
let arg0: DOMString;
|
|
let strval = jsval_to_str(cx, (*argv.offset(0)));
|
|
if strval.is_err() {
|
|
return 0;
|
|
}
|
|
arg0 = str(strval.unwrap());
|
|
|
|
let arg1: DOMString;
|
|
let strval = jsval_to_str(cx, (*argv.offset(1)));
|
|
if strval.is_err() {
|
|
return 0;
|
|
}
|
|
arg1 = str(strval.unwrap());
|
|
|
|
do node.as_mut_element |elem| {
|
|
elem.set_attr(&arg0, &arg1);
|
|
};
|
|
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
extern fn HTMLImageElement_getWidth(cx: *JSContext, _argc: c_uint, vp: *mut JSVal) -> JSBool {
|
|
unsafe {
|
|
let obj = JS_THIS_OBJECT(cx, cast::transmute(vp));
|
|
if obj.is_null() {
|
|
return 0;
|
|
}
|
|
|
|
let node = unwrap(obj);
|
|
let width = match node.type_id() {
|
|
ElementNodeTypeId(HTMLImageElementTypeId) => {
|
|
let page = page_from_context(cx);
|
|
let (port, chan) = comm::stream();
|
|
// TODO(tkuehn): currently this just queries top-level page's layout. Need to handle subframes.
|
|
match (*page).query_layout(ContentBoxQuery(node, chan), port) {
|
|
Ok(ContentBoxResponse(rect)) => rect.size.width.to_nearest_px(),
|
|
Err(()) => 0
|
|
}
|
|
// TODO: if nothing is being rendered(?), return zero dimensions
|
|
}
|
|
ElementNodeTypeId(_) => fail!(~"why is this not an image element?"),
|
|
_ => fail!(~"why is this not an element?")
|
|
};
|
|
|
|
*vp = RUST_INT_TO_JSVAL(
|
|
(width & (i32::max_value as int)) as libc::c_int);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
extern fn HTMLImageElement_setWidth(cx: *JSContext, _argc: c_uint, vp: *mut JSVal) -> JSBool {
|
|
unsafe {
|
|
let obj = JS_THIS_OBJECT(cx, cast::transmute(vp));
|
|
if obj.is_null() {
|
|
return 0;
|
|
}
|
|
|
|
let node = unwrap(obj);
|
|
match node.type_id() {
|
|
ElementNodeTypeId(HTMLImageElementTypeId) => {
|
|
do node.as_mut_element |elem| {
|
|
let arg = ptr::offset(JS_ARGV(cx, cast::transmute(vp)), 0);
|
|
elem.set_attr(&str(~"width"),
|
|
&str((RUST_JSVAL_TO_INT(*arg) as int).to_str()))
|
|
}
|
|
}
|
|
ElementNodeTypeId(_) => fail!(~"why is this not an image element?"),
|
|
_ => fail!(~"why is this not an element?")
|
|
};
|
|
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
extern fn getTagName(cx: *JSContext, _argc: c_uint, vp: *mut JSVal) -> JSBool {
|
|
unsafe {
|
|
let obj = JS_THIS_OBJECT(cx, cast::transmute(vp));
|
|
if obj.is_null() {
|
|
return 0;
|
|
}
|
|
|
|
let node = unwrap(obj);
|
|
do node.with_imm_element |elem| {
|
|
let s = str(elem.tag_name.clone());
|
|
*vp = domstring_to_jsval(cx, &s);
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
pub fn create(cx: *JSContext, node: &mut AbstractNode<ScriptView>) -> jsobj {
|
|
let proto = match node.type_id() {
|
|
ElementNodeTypeId(HTMLDivElementTypeId) => ~"HTMLDivElement",
|
|
ElementNodeTypeId(HTMLHeadElementTypeId) => ~"HTMLHeadElement",
|
|
ElementNodeTypeId(HTMLImageElementTypeId) => ~"HTMLImageElement",
|
|
ElementNodeTypeId(HTMLScriptElementTypeId) => ~"HTMLScriptElement",
|
|
ElementNodeTypeId(_) => ~"HTMLElement",
|
|
_ => fail!(~"element::create only handles elements")
|
|
};
|
|
|
|
//XXXjdm the parent should probably be the node parent instead of the global
|
|
//TODO error checking
|
|
let compartment = utils::get_compartment(cx);
|
|
let obj = compartment.new_object_with_proto(~"GenericElementInstance",
|
|
proto,
|
|
compartment.global_obj.ptr).unwrap();
|
|
|
|
let cache = node.get_wrappercache();
|
|
assert!(cache.get_wrapper().is_null());
|
|
cache.set_wrapper(obj.ptr);
|
|
|
|
unsafe {
|
|
let raw_ptr = node.raw_object() as *libc::c_void;
|
|
JS_SetReservedSlot(obj.ptr, DOM_OBJECT_SLOT as u32, RUST_PRIVATE_TO_JSVAL(raw_ptr));
|
|
}
|
|
|
|
return obj;
|
|
}
|