mirror of
https://github.com/servo/servo.git
synced 2025-08-05 13:40:08 +01:00
Add window.setTimeout. Convert all content node bindings to use RCU write pointer. Store a bundle of Node + NodeScope in each node JS object.
This commit is contained in:
parent
d4b2810af1
commit
4db345c3b3
9 changed files with 184 additions and 54 deletions
|
@ -4,7 +4,7 @@
|
|||
"]
|
||||
|
||||
export ContentTask;
|
||||
export ControlMsg, ExecuteMsg, ParseMsg, ExitMsg;
|
||||
export ControlMsg, ExecuteMsg, ParseMsg, ExitMsg, Timer;
|
||||
export PingMsg, PongMsg;
|
||||
|
||||
import std::arc::{arc, clone};
|
||||
|
@ -24,7 +24,7 @@ import layout::layout_task;
|
|||
import layout_task::{LayoutTask, BuildMsg};
|
||||
|
||||
import jsrt = js::rust::rt;
|
||||
import js::rust::methods;
|
||||
import js::rust::{cx, methods};
|
||||
import js::global::{global_class, debug_fns};
|
||||
|
||||
import either::{Either, Left, Right};
|
||||
|
@ -40,9 +40,16 @@ import url_to_str = std::net::url::to_str;
|
|||
import util::url::make_url;
|
||||
import task::{task, SingleThreaded};
|
||||
|
||||
import js::glue::bindgen::RUST_JSVAL_TO_OBJECT;
|
||||
import js::JSVAL_NULL;
|
||||
import js::jsapi::jsval;
|
||||
import js::jsapi::bindgen::JS_CallFunctionValue;
|
||||
import ptr::null;
|
||||
|
||||
enum ControlMsg {
|
||||
ParseMsg(url),
|
||||
ExecuteMsg(url),
|
||||
Timer(~dom::bindings::window::TimerData),
|
||||
ExitMsg
|
||||
}
|
||||
|
||||
|
@ -79,6 +86,7 @@ struct Content<C:Compositor> {
|
|||
|
||||
let scope: NodeScope;
|
||||
let jsrt: jsrt;
|
||||
let cx: cx;
|
||||
|
||||
let mut document: option<@Document>;
|
||||
let mut window: option<@Window>;
|
||||
|
@ -86,6 +94,8 @@ struct Content<C:Compositor> {
|
|||
|
||||
let resource_task: ResourceTask;
|
||||
|
||||
let compartment: option<compartment>;
|
||||
|
||||
new(layout_task: LayoutTask, +compositor: C, from_master: Port<ControlMsg>,
|
||||
resource_task: ResourceTask) {
|
||||
self.layout_task = layout_task;
|
||||
|
@ -95,6 +105,7 @@ struct Content<C:Compositor> {
|
|||
|
||||
self.scope = NodeScope();
|
||||
self.jsrt = jsrt();
|
||||
self.cx = self.jsrt.cx();
|
||||
|
||||
self.document = none;
|
||||
self.window = none;
|
||||
|
@ -103,6 +114,13 @@ struct Content<C:Compositor> {
|
|||
self.compositor.add_event_listener(self.event_port.chan());
|
||||
|
||||
self.resource_task = resource_task;
|
||||
|
||||
self.cx.set_default_options_and_version();
|
||||
self.cx.set_logging_error_reporter();
|
||||
self.compartment = match self.cx.new_compartment(global_class) {
|
||||
ok(c) => some(c),
|
||||
err(()) => none
|
||||
};
|
||||
}
|
||||
|
||||
fn start() {
|
||||
|
@ -140,30 +158,42 @@ struct Content<C:Compositor> {
|
|||
|
||||
#debug["js_scripts: %?", js_scripts];
|
||||
|
||||
let document = Document(root, css_rules);
|
||||
let window = Window();
|
||||
let document = Document(root, self.scope, css_rules);
|
||||
let window = Window(self.from_master);
|
||||
self.relayout(document, &url);
|
||||
self.document = some(@document);
|
||||
self.window = some(@window);
|
||||
self.doc_url = some(copy url);
|
||||
|
||||
//XXXjdm it was easier to duplicate the relevant ExecuteMsg code;
|
||||
// they should be merged somehow in the future.
|
||||
let compartment = option::expect(self.compartment, ~"TODO error checking");
|
||||
compartment.define_functions(debug_fns);
|
||||
define_bindings(*compartment,
|
||||
option::get(self.document),
|
||||
option::get(self.window));
|
||||
|
||||
for vec::each(js_scripts) |bytes| {
|
||||
let cx = self.jsrt.cx();
|
||||
cx.set_default_options_and_version();
|
||||
cx.set_logging_error_reporter();
|
||||
cx.new_compartment(global_class).chain(|compartment| {
|
||||
compartment.define_functions(debug_fns);
|
||||
define_bindings(*compartment, option::get(self.document),
|
||||
option::get(self.window));
|
||||
cx.evaluate_script(compartment.global_obj, bytes, ~"???", 1u)
|
||||
});
|
||||
self.cx.evaluate_script(compartment.global_obj, bytes, ~"???", 1u);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Timer(timerData) => {
|
||||
let compartment = option::expect(self.compartment, ~"TODO error checking");
|
||||
let thisValue = if timerData.args.len() > 0 {
|
||||
RUST_JSVAL_TO_OBJECT(unsafe { timerData.args.shift() })
|
||||
} else {
|
||||
compartment.global_obj.ptr
|
||||
};
|
||||
let _rval = JSVAL_NULL;
|
||||
//TODO: support extra args. requires passing a *jsval argv
|
||||
JS_CallFunctionValue(self.cx.ptr, thisValue, timerData.funval,
|
||||
0, null(), ptr::addr_of(_rval));
|
||||
self.relayout(*option::get(self.document), &option::get(self.doc_url));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
ExecuteMsg(url) => {
|
||||
#debug["content: Received url `%s` to execute", url_to_str(url)];
|
||||
|
||||
|
@ -172,13 +202,9 @@ struct Content<C:Compositor> {
|
|||
println(#fmt["Error opening %s: %s", url_to_str(url), msg]);
|
||||
}
|
||||
ok(bytes) => {
|
||||
let cx = self.jsrt.cx();
|
||||
cx.set_default_options_and_version();
|
||||
cx.set_logging_error_reporter();
|
||||
cx.new_compartment(global_class).chain(|compartment| {
|
||||
compartment.define_functions(debug_fns);
|
||||
cx.evaluate_script(compartment.global_obj, bytes, url.path, 1u)
|
||||
});
|
||||
let compartment = option::expect(self.compartment, ~"TODO error checking");
|
||||
compartment.define_functions(debug_fns);
|
||||
self.cx.evaluate_script(compartment.global_obj, bytes, url.path, 1u);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -14,20 +14,44 @@ import ptr::null;
|
|||
import bindings;
|
||||
import std::arc::arc;
|
||||
import style::Stylesheet;
|
||||
import comm::{Port, Chan};
|
||||
import content::content_task::{ControlMsg, Timer};
|
||||
|
||||
enum TimerControlMsg {
|
||||
Fire(~dom::bindings::window::TimerData),
|
||||
Close
|
||||
}
|
||||
|
||||
struct Window {
|
||||
let unused: int;
|
||||
new() {
|
||||
self.unused = 0;
|
||||
let timer_chan: Chan<TimerControlMsg>;
|
||||
|
||||
new(content_port: Port<ControlMsg>) {
|
||||
let content_chan = chan(content_port);
|
||||
|
||||
self.timer_chan = do task::spawn_listener |timer_port: Port<TimerControlMsg>| {
|
||||
loop {
|
||||
match timer_port.recv() {
|
||||
Close => break,
|
||||
Fire(td) => {
|
||||
content_chan.send(Timer(td));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
drop {
|
||||
self.timer_chan.send(Close);
|
||||
}
|
||||
}
|
||||
|
||||
struct Document {
|
||||
let root: Node;
|
||||
let scope: NodeScope;
|
||||
let css_rules: arc<Stylesheet>;
|
||||
|
||||
new(root: Node, -css_rules: Stylesheet) {
|
||||
new(root: Node, scope: NodeScope, -css_rules: Stylesheet) {
|
||||
self.root = root;
|
||||
self.scope = scope;
|
||||
self.css_rules = arc(css_rules);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,8 +61,10 @@ enum Element = int;
|
|||
}*/
|
||||
|
||||
extern fn getDocumentElement(cx: *JSContext, obj: *JSObject, _id: jsid, rval: *mut jsval) -> JSBool unsafe {
|
||||
let node = (*unwrap(obj)).payload.root;
|
||||
*rval = RUST_OBJECT_TO_JSVAL(node::create(cx, node).ptr);
|
||||
let box = unwrap(obj);
|
||||
let node = (*box).payload.root;
|
||||
let scope = (*box).payload.scope;
|
||||
*rval = RUST_OBJECT_TO_JSVAL(node::create(cx, node, scope).ptr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,8 @@ import js::jsapi::bindgen::*;
|
|||
import js::glue::bindgen::*;
|
||||
import js::crust::{JS_PropertyStub, JS_StrictPropertyStub, JS_EnumerateStub, JS_ConvertStub};
|
||||
|
||||
import dom::base::{Node, Element};
|
||||
import dom::base::{Node, NodeScope, Element};
|
||||
import node::NodeBundle;
|
||||
import utils::{rust_box, squirrel_away_unique, get_compartment, domstring_to_jsval, str};
|
||||
import libc::c_uint;
|
||||
import ptr::null;
|
||||
|
@ -22,7 +23,7 @@ extern fn finalize(_fop: *JSFreeOp, obj: *JSObject) {
|
|||
#debug("element finalize!");
|
||||
unsafe {
|
||||
let val = JS_GetReservedSlot(obj, 0);
|
||||
let _node: ~Node = unsafe::reinterpret_cast(RUST_JSVAL_TO_PRIVATE(val));
|
||||
let _node: ~NodeBundle = unsafe::reinterpret_cast(RUST_JSVAL_TO_PRIVATE(val));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -62,7 +63,8 @@ fn init(compartment: bare_compartment) {
|
|||
|
||||
extern fn HTMLImageElement_getWidth(_cx: *JSContext, obj: *JSObject, _id: jsid,
|
||||
rval: *mut jsval) -> JSBool unsafe {
|
||||
let width = (*unwrap(obj)).payload.read(|nd| {
|
||||
let bundle = unwrap(obj);
|
||||
let width = (*bundle).payload.scope.write((*bundle).payload.node, |nd| {
|
||||
match nd.kind {
|
||||
~Element(ed) => {
|
||||
match ed.kind {
|
||||
|
@ -80,7 +82,8 @@ extern fn HTMLImageElement_getWidth(_cx: *JSContext, obj: *JSObject, _id: jsid,
|
|||
|
||||
extern fn HTMLImageElement_setWidth(_cx: *JSContext, obj: *JSObject, _id: jsid,
|
||||
_strict: JSBool, vp: *jsval) -> JSBool unsafe {
|
||||
let width = (*unwrap(obj)).payload.read(|nd| {
|
||||
let bundle = unwrap(obj);
|
||||
do (*bundle).payload.scope.write((*bundle).payload.node) |nd| {
|
||||
match nd.kind {
|
||||
~Element(ed) => {
|
||||
match ed.kind {
|
||||
|
@ -91,14 +94,15 @@ extern fn HTMLImageElement_setWidth(_cx: *JSContext, obj: *JSObject, _id: jsid,
|
|||
}
|
||||
_ => fail ~"why is this not an element?"
|
||||
}
|
||||
});
|
||||
};
|
||||
return 1;
|
||||
}
|
||||
|
||||
extern fn getTagName(cx: *JSContext, obj: *JSObject, _id: jsid, rval: *mut jsval)
|
||||
-> JSBool {
|
||||
unsafe {
|
||||
(*unwrap(obj)).payload.read(|nd| {
|
||||
let bundle = unwrap(obj);
|
||||
do (*bundle).payload.scope.write((*bundle).payload.node) |nd| {
|
||||
match nd.kind {
|
||||
~Element(ed) => {
|
||||
let s = str(copy ed.tag_name);
|
||||
|
@ -109,13 +113,13 @@ extern fn getTagName(cx: *JSContext, obj: *JSObject, _id: jsid, rval: *mut jsval
|
|||
*rval = JSVAL_NULL;
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
fn create(cx: *JSContext, node: Node) -> jsobj unsafe {
|
||||
let proto = node.read(|nd| {
|
||||
fn create(cx: *JSContext, node: Node, scope: NodeScope) -> jsobj unsafe {
|
||||
let proto = scope.write(node, |nd| {
|
||||
match nd.kind {
|
||||
~Element(ed) => {
|
||||
match ed.kind {
|
||||
|
@ -138,7 +142,8 @@ fn create(cx: *JSContext, node: Node) -> jsobj unsafe {
|
|||
(*compartment).global_obj.ptr));
|
||||
|
||||
unsafe {
|
||||
let raw_ptr: *libc::c_void = unsafe::reinterpret_cast(squirrel_away_unique(~node));
|
||||
let raw_ptr: *libc::c_void =
|
||||
unsafe::reinterpret_cast(squirrel_away_unique(~NodeBundle(node, scope)));
|
||||
JS_SetReservedSlot(obj.ptr, 0, RUST_PRIVATE_TO_JSVAL(raw_ptr));
|
||||
}
|
||||
return obj;
|
||||
|
|
|
@ -9,7 +9,7 @@ import js::jsapi::bindgen::*;
|
|||
import js::glue::bindgen::*;
|
||||
import js::crust::{JS_PropertyStub, JS_StrictPropertyStub, JS_EnumerateStub, JS_ConvertStub};
|
||||
|
||||
import dom::base::{Node, Element, Text};
|
||||
import dom::base::{Node, NodeScope, Element, Text};
|
||||
import utils::{rust_box, squirrel_away_unique, get_compartment, domstring_to_jsval, str};
|
||||
import libc::c_uint;
|
||||
import ptr::null;
|
||||
|
@ -35,11 +35,11 @@ fn init(compartment: bare_compartment) {
|
|||
});
|
||||
}
|
||||
|
||||
fn create(cx: *JSContext, node: Node) -> jsobj unsafe {
|
||||
do node.read |nd| {
|
||||
fn create(cx: *JSContext, node: Node, scope: NodeScope) -> jsobj unsafe {
|
||||
do scope.write(node) |nd| {
|
||||
match nd.kind {
|
||||
~Element(ed) => {
|
||||
element::create(cx, node)
|
||||
element::create(cx, node, scope)
|
||||
}
|
||||
|
||||
~Text(s) => {
|
||||
|
@ -49,41 +49,53 @@ fn create(cx: *JSContext, node: Node) -> jsobj unsafe {
|
|||
}
|
||||
}
|
||||
|
||||
unsafe fn unwrap(obj: *JSObject) -> *rust_box<Node> {
|
||||
struct NodeBundle {
|
||||
let node: Node;
|
||||
let scope: NodeScope;
|
||||
|
||||
new(n: Node, s: NodeScope) {
|
||||
self.node = n;
|
||||
self.scope = s;
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn unwrap(obj: *JSObject) -> *rust_box<NodeBundle> {
|
||||
let val = JS_GetReservedSlot(obj, 0);
|
||||
unsafe::reinterpret_cast(RUST_JSVAL_TO_PRIVATE(val))
|
||||
}
|
||||
|
||||
extern fn getFirstChild(cx: *JSContext, obj: *JSObject, _id: jsid, rval: *mut jsval) -> JSBool {
|
||||
unsafe {
|
||||
(*unwrap(obj)).payload.read(|nd| {
|
||||
let bundle = unwrap(obj);
|
||||
do (*bundle).payload.scope.write((*bundle).payload.node) |nd| {
|
||||
match nd.tree.first_child {
|
||||
some(n) => {
|
||||
let obj = create(cx, n).ptr;
|
||||
let obj = create(cx, n, (*bundle).payload.scope).ptr;
|
||||
*rval = RUST_OBJECT_TO_JSVAL(obj);
|
||||
}
|
||||
none => {
|
||||
*rval = JSVAL_NULL;
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
extern fn getNextSibling(cx: *JSContext, obj: *JSObject, _id: jsid, rval: *mut jsval) -> JSBool {
|
||||
unsafe {
|
||||
(*unwrap(obj)).payload.read(|nd| {
|
||||
let bundle = unwrap(obj);
|
||||
do (*bundle).payload.scope.write((*bundle).payload.node) |nd| {
|
||||
match nd.tree.next_sibling {
|
||||
some(n) => {
|
||||
let obj = create(cx, n).ptr;
|
||||
let obj = create(cx, n, (*bundle).payload.scope).ptr;
|
||||
*rval = RUST_OBJECT_TO_JSVAL(obj);
|
||||
}
|
||||
none => {
|
||||
*rval = JSVAL_NULL;
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import js::rust::{bare_compartment, methods};
|
||||
import js::{JS_ARGV, JSCLASS_HAS_RESERVED_SLOTS, JSPROP_ENUMERATE, JSPROP_SHARED, JSVAL_NULL, JS_THIS_OBJECT,
|
||||
JS_SET_RVAL};
|
||||
import js::{JS_ARGV, JSCLASS_HAS_RESERVED_SLOTS, JSPROP_ENUMERATE, JSPROP_SHARED, JSVAL_NULL,
|
||||
JS_THIS_OBJECT, JS_SET_RVAL};
|
||||
import js::jsapi::{JSContext, jsval, JSObject, JSBool, jsid, JSClass, JSFreeOp};
|
||||
import js::jsapi::bindgen::{JS_ValueToString, JS_GetStringCharsZAndLength, JS_ReportError,
|
||||
JS_GetReservedSlot, JS_SetReservedSlot, JS_NewStringCopyN,
|
||||
|
@ -8,12 +8,14 @@ import js::jsapi::bindgen::{JS_ValueToString, JS_GetStringCharsZAndLength, JS_Re
|
|||
import js::glue::bindgen::*;
|
||||
import js::global::jsval_to_rust_str;
|
||||
import js::crust::{JS_PropertyStub, JS_StrictPropertyStub, JS_EnumerateStub, JS_ConvertStub, JS_ResolveStub};
|
||||
import js::glue::bindgen::RUST_JSVAL_TO_INT;
|
||||
import result::{result, ok, err};
|
||||
import ptr::null;
|
||||
import libc::c_uint;
|
||||
import utils::{rust_box, squirrel_away, jsval_to_str};
|
||||
import bindings::node::create;
|
||||
import base::{Node, Window};
|
||||
import dvec::{DVec, dvec};
|
||||
|
||||
extern fn alert(cx: *JSContext, argc: c_uint, vp: *jsval) -> JSBool {
|
||||
unsafe {
|
||||
|
@ -28,6 +30,40 @@ extern fn alert(cx: *JSContext, argc: c_uint, vp: *jsval) -> JSBool {
|
|||
1_i32
|
||||
}
|
||||
|
||||
// Holder for the various JS values associated with setTimeout
|
||||
// (ie. function value to invoke and all arguments to pass
|
||||
// to the function when calling it)
|
||||
struct TimerData {
|
||||
let funval: jsval;
|
||||
let args: DVec<jsval>;
|
||||
new(argc: c_uint, argv: *jsval) unsafe {
|
||||
self.funval = *argv;
|
||||
self.args = dvec();
|
||||
let mut i = 2;
|
||||
while i < argc as uint {
|
||||
self.args.push(*ptr::offset(argv, i));
|
||||
i += 1;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
extern fn setTimeout(cx: *JSContext, argc: c_uint, vp: *jsval) -> JSBool unsafe {
|
||||
let argv = JS_ARGV(cx, vp);
|
||||
assert (argc >= 2);
|
||||
|
||||
//TODO: don't crash when passed a non-integer value for the timeout
|
||||
|
||||
// Post a delayed message to the per-window timer task; it will dispatch it
|
||||
// to the relevant content handler that will deal with it.
|
||||
std::timer::delayed_send(std::uv_global_loop::get(),
|
||||
RUST_JSVAL_TO_INT(*ptr::offset(argv, 1)) as uint,
|
||||
(*unwrap(JS_THIS_OBJECT(cx, vp))).payload.timer_chan,
|
||||
base::Fire(~TimerData(argc, argv)));
|
||||
|
||||
JS_SET_RVAL(cx, vp, JSVAL_NULL);
|
||||
return 1;
|
||||
}
|
||||
|
||||
unsafe fn unwrap(obj: *JSObject) -> *rust_box<Window> {
|
||||
let val = JS_GetReservedSlot(obj, 0);
|
||||
unsafe::reinterpret_cast(RUST_JSVAL_TO_PRIVATE(val))
|
||||
|
@ -53,8 +89,12 @@ fn init(compartment: bare_compartment, win: @Window) {
|
|||
let methods = ~[{name: compartment.add_name(~"alert"),
|
||||
call: alert,
|
||||
nargs: 1,
|
||||
flags: 0},
|
||||
{name: compartment.add_name(~"setTimeout"),
|
||||
call: setTimeout,
|
||||
nargs: 2,
|
||||
flags: 0}];
|
||||
|
||||
|
||||
vec::as_buf(methods, |fns, _len| {
|
||||
JS_DefineFunctions(compartment.cx.ptr, proto.ptr, fns);
|
||||
});
|
||||
|
|
|
@ -1,6 +1,15 @@
|
|||
function setWidth(w, i) {
|
||||
var elem = document.documentElement.firstChild;
|
||||
elem.width = w;
|
||||
debug(elem.width);
|
||||
w += i;
|
||||
if (w == 0 || w == 1000)
|
||||
i *= -1;
|
||||
window.setTimeout(function() { setWidth(w, i); }, 50);
|
||||
}
|
||||
|
||||
var elem = document.documentElement.firstChild;
|
||||
debug(elem.tagName);
|
||||
debug(elem instanceof HTMLImageElement);
|
||||
debug(elem.width);
|
||||
elem.width = 1000;
|
||||
debug(elem.width);
|
||||
setWidth(1000, -10);
|
1
src/test/test_timeout.html
Normal file
1
src/test/test_timeout.html
Normal file
|
@ -0,0 +1 @@
|
|||
<div></div><script src="test_timeout.js"></script>
|
11
src/test/test_timeout.js
Normal file
11
src/test/test_timeout.js
Normal file
|
@ -0,0 +1,11 @@
|
|||
function foo(i) {
|
||||
window.alert("timeout " + i);
|
||||
if (i == 10)
|
||||
window.alert("timeouts finished");
|
||||
else
|
||||
window.setTimeout(function() { foo(i + 1); }, 1000);
|
||||
}
|
||||
|
||||
window.alert("beginning timeouts");
|
||||
window.setTimeout(function() { foo(0); }, 1000);
|
||||
window.alert("timeouts begun");
|
Loading…
Add table
Add a link
Reference in a new issue