Refactor the script task to conform to current coding style

This commit is contained in:
Patrick Walton 2013-05-16 14:33:22 -07:00
parent ad5bfd5297
commit 2ba5dfbfe3
14 changed files with 381 additions and 299 deletions

View file

@ -12,8 +12,8 @@ use scripting::script_task::{task_from_context, global_script_context};
pub impl ClientRect { pub impl ClientRect {
pub fn init_wrapper(@mut self) { pub fn init_wrapper(@mut self) {
let script_context = global_script_context(); let script_context = global_script_context();
let cx = script_context.compartment.get().cx.ptr; let cx = script_context.js_compartment.cx.ptr;
let owner = script_context.window.get(); let owner = script_context.root_frame.get_ref().window;
let cache = owner.get_wrappercache(); let cache = owner.get_wrappercache();
let scope = cache.get_wrapper(); let scope = cache.get_wrapper();
self.wrap_object_shared(cx, scope); self.wrap_object_shared(cx, scope);
@ -37,7 +37,7 @@ impl BindingObject for ClientRect {
fn GetParentObject(&self, cx: *JSContext) -> @mut CacheableWrapper { fn GetParentObject(&self, cx: *JSContext) -> @mut CacheableWrapper {
let script_context = task_from_context(cx); let script_context = task_from_context(cx);
unsafe { unsafe {
(*script_context).window.get() as @mut CacheableWrapper (*script_context).root_frame.get_ref().window as @mut CacheableWrapper
} }
} }
} }

View file

@ -11,8 +11,8 @@ use scripting::script_task::{task_from_context, global_script_context};
pub impl ClientRectList { pub impl ClientRectList {
fn init_wrapper(@mut self) { fn init_wrapper(@mut self) {
let script_context = global_script_context(); let script_context = global_script_context();
let cx = script_context.compartment.get().cx.ptr; let cx = script_context.js_compartment.cx.ptr;
let owner = script_context.window.get(); let owner = script_context.root_frame.get_ref().window;
let cache = owner.get_wrappercache(); let cache = owner.get_wrappercache();
let scope = cache.get_wrapper(); let scope = cache.get_wrapper();
self.wrap_object_shared(cx, scope); self.wrap_object_shared(cx, scope);
@ -21,7 +21,9 @@ pub impl ClientRectList {
impl CacheableWrapper for ClientRectList { impl CacheableWrapper for ClientRectList {
fn get_wrappercache(&mut self) -> &mut WrapperCache { fn get_wrappercache(&mut self) -> &mut WrapperCache {
unsafe { cast::transmute(&self.wrapper) } unsafe {
cast::transmute(&self.wrapper)
}
} }
fn wrap_object_shared(@mut self, cx: *JSContext, scope: *JSObject) -> *JSObject { fn wrap_object_shared(@mut self, cx: *JSContext, scope: *JSObject) -> *JSObject {
@ -33,6 +35,8 @@ impl CacheableWrapper for ClientRectList {
impl BindingObject for ClientRectList { impl BindingObject for ClientRectList {
fn GetParentObject(&self, cx: *JSContext) -> @mut CacheableWrapper { fn GetParentObject(&self, cx: *JSContext) -> @mut CacheableWrapper {
let script_context = task_from_context(cx); let script_context = task_from_context(cx);
unsafe { (*script_context).window.get() as @mut CacheableWrapper } unsafe {
(*script_context).root_frame.get_ref().window as @mut CacheableWrapper
}
} }
} }

View file

@ -3615,7 +3615,7 @@ class CGClassConstructHook(CGAbstractExternMethod):
// or through unwrapping a slot or something). We'll punt and get the Window // or through unwrapping a slot or something). We'll punt and get the Window
// from the context for now. // from the context for now.
let script_context = task_from_context(cx); let script_context = task_from_context(cx);
let global = (*script_context).window.get(); let global = (*script_context).root_frame.get_ref().window;
let obj = global.get_wrappercache().get_wrapper(); let obj = global.get_wrappercache().get_wrapper();
""" """
preArgs = ["global"] preArgs = ["global"]

View file

@ -146,6 +146,9 @@ impl CacheableWrapper for Document {
fn wrap_object_shared(@mut self, cx: *JSContext, _scope: *JSObject) -> *JSObject { fn wrap_object_shared(@mut self, cx: *JSContext, _scope: *JSObject) -> *JSObject {
let script_context = task_from_context(cx); let script_context = task_from_context(cx);
unsafe { create((*script_context).compartment.get(), self) } unsafe {
create((*script_context).js_compartment, self)
}
} }
} }

View file

@ -12,8 +12,8 @@ use scripting::script_task::{task_from_context, global_script_context};
pub impl Event_ { pub impl Event_ {
pub fn init_wrapper(@mut self) { pub fn init_wrapper(@mut self) {
let script_context = global_script_context(); let script_context = global_script_context();
let cx = script_context.compartment.get().cx.ptr; let cx = script_context.js_compartment.cx.ptr;
let owner = script_context.window.get(); let owner = script_context.root_frame.get_ref().window;
let cache = owner.get_wrappercache(); let cache = owner.get_wrappercache();
let scope = cache.get_wrapper(); let scope = cache.get_wrapper();
self.wrap_object_shared(cx, scope); self.wrap_object_shared(cx, scope);
@ -34,7 +34,9 @@ impl CacheableWrapper for Event_ {
impl BindingObject for Event_ { impl BindingObject for Event_ {
fn GetParentObject(&self, cx: *JSContext) -> @mut CacheableWrapper { fn GetParentObject(&self, cx: *JSContext) -> @mut CacheableWrapper {
let script_context = task_from_context(cx); let script_context = task_from_context(cx);
unsafe { (*script_context).window.get() as @mut CacheableWrapper } unsafe {
(*script_context).root_frame.get_ref().window as @mut CacheableWrapper
}
} }
} }

View file

@ -5,6 +5,7 @@
use dom::bindings::codegen::EventTargetBinding; use dom::bindings::codegen::EventTargetBinding;
use dom::bindings::utils::{CacheableWrapper, WrapperCache, BindingObject, DerivedWrapper}; use dom::bindings::utils::{CacheableWrapper, WrapperCache, BindingObject, DerivedWrapper};
use dom::eventtarget::EventTarget; use dom::eventtarget::EventTarget;
use js::glue::bindgen::RUST_OBJECT_TO_JSVAL; use js::glue::bindgen::RUST_OBJECT_TO_JSVAL;
use js::jsapi::{JSObject, JSContext, JSVal}; use js::jsapi::{JSObject, JSContext, JSVal};
use scripting::script_task::{task_from_context, global_script_context}; use scripting::script_task::{task_from_context, global_script_context};
@ -12,8 +13,8 @@ use scripting::script_task::{task_from_context, global_script_context};
pub impl EventTarget { pub impl EventTarget {
pub fn init_wrapper(@mut self) { pub fn init_wrapper(@mut self) {
let script_context = global_script_context(); let script_context = global_script_context();
let cx = script_context.compartment.get().cx.ptr; let cx = script_context.js_compartment.cx.ptr;
let owner = script_context.window.get(); let owner = script_context.root_frame.get_ref().window;
let cache = owner.get_wrappercache(); let cache = owner.get_wrappercache();
let scope = cache.get_wrapper(); let scope = cache.get_wrapper();
self.wrap_object_shared(cx, scope); self.wrap_object_shared(cx, scope);
@ -34,7 +35,9 @@ impl CacheableWrapper for EventTarget {
impl BindingObject for EventTarget { impl BindingObject for EventTarget {
fn GetParentObject(&self, cx: *JSContext) -> @mut CacheableWrapper { fn GetParentObject(&self, cx: *JSContext) -> @mut CacheableWrapper {
let script_context = task_from_context(cx); let script_context = task_from_context(cx);
unsafe { (*script_context).window.get() as @mut CacheableWrapper } unsafe {
(*script_context).root_frame.get_ref().window as @mut CacheableWrapper
}
} }
} }
@ -48,7 +51,9 @@ impl DerivedWrapper for EventTarget {
if obj.is_null() { if obj.is_null() {
return 0; return 0;
} else { } else {
unsafe { *vp = RUST_OBJECT_TO_JSVAL(obj) }; unsafe {
*vp = RUST_OBJECT_TO_JSVAL(obj)
};
return 1; return 1;
} }
} }

View file

@ -11,8 +11,8 @@ use scripting::script_task::{task_from_context, global_script_context};
pub impl HTMLCollection { pub impl HTMLCollection {
fn init_wrapper(@mut self) { fn init_wrapper(@mut self) {
let script_context = global_script_context(); let script_context = global_script_context();
let cx = script_context.compartment.get().cx.ptr; let cx = script_context.js_compartment.cx.ptr;
let owner = script_context.window.get(); let owner = script_context.root_frame.get_ref().window;
let cache = owner.get_wrappercache(); let cache = owner.get_wrappercache();
let scope = cache.get_wrapper(); let scope = cache.get_wrapper();
self.wrap_object_shared(cx, scope); self.wrap_object_shared(cx, scope);
@ -22,13 +22,17 @@ pub impl HTMLCollection {
impl BindingObject for HTMLCollection { impl BindingObject for HTMLCollection {
fn GetParentObject(&self, cx: *JSContext) -> @mut CacheableWrapper { fn GetParentObject(&self, cx: *JSContext) -> @mut CacheableWrapper {
let script_context = task_from_context(cx); let script_context = task_from_context(cx);
unsafe { (*script_context).window.get() as @mut CacheableWrapper } unsafe {
(*script_context).root_frame.get_ref().window as @mut CacheableWrapper
}
} }
} }
impl CacheableWrapper for HTMLCollection { impl CacheableWrapper for HTMLCollection {
fn get_wrappercache(&mut self) -> &mut WrapperCache { fn get_wrappercache(&mut self) -> &mut WrapperCache {
unsafe { cast::transmute(&self.wrapper) } unsafe {
cast::transmute(&self.wrapper)
}
} }
fn wrap_object_shared(@mut self, cx: *JSContext, scope: *JSObject) -> *JSObject { fn wrap_object_shared(@mut self, cx: *JSContext, scope: *JSObject) -> *JSObject {

View file

@ -164,8 +164,7 @@ pub unsafe fn domstring_to_jsval(cx: *JSContext, string: &DOMString) -> JSVal {
pub fn get_compartment(cx: *JSContext) -> @mut Compartment { pub fn get_compartment(cx: *JSContext) -> @mut Compartment {
unsafe { unsafe {
let script_context = task_from_context(cx); let script_context = task_from_context(cx);
let compartment = (*script_context).compartment.expect(~"Should always have compartment \ let compartment = (*script_context).js_compartment;
when executing JS code");
assert!(cx == compartment.cx.ptr); assert!(cx == compartment.cx.ptr);
compartment compartment
} }

View file

@ -25,7 +25,7 @@ pub fn Document(root: AbstractNode, window: Option<@mut Window>) -> @mut Documen
wrapper: WrapperCache::new(), wrapper: WrapperCache::new(),
window: window window: window
}; };
let compartment = global_script_context().compartment.get(); let compartment = global_script_context().js_compartment;
do root.with_base |base| { do root.with_base |base| {
assert!(base.wrapper.get_wrapper().is_not_null()); assert!(base.wrapper.get_wrapper().is_not_null());
let rootable = base.wrapper.get_rootable(); let rootable = base.wrapper.get_rootable();
@ -38,7 +38,7 @@ pub fn Document(root: AbstractNode, window: Option<@mut Window>) -> @mut Documen
#[unsafe_destructor] #[unsafe_destructor]
impl Drop for Document { impl Drop for Document {
fn finalize(&self) { fn finalize(&self) {
let compartment = global_script_context().compartment.get(); let compartment = global_script_context().js_compartment;
do self.root.with_base |base| { do self.root.with_base |base| {
assert!(base.wrapper.get_wrapper().is_not_null()); assert!(base.wrapper.get_wrapper().is_not_null());
let rootable = base.wrapper.get_rootable(); let rootable = base.wrapper.get_rootable();

View file

@ -22,7 +22,7 @@ impl DOMParser {
wrapper: WrapperCache::new() wrapper: WrapperCache::new()
}; };
let cx = global_script_context().compartment.get().cx.ptr; let cx = global_script_context().js_compartment.cx.ptr;
let cache = owner.get_wrappercache(); let cache = owner.get_wrappercache();
let scope = cache.get_wrapper(); let scope = cache.get_wrapper();
parser.wrap_object_shared(cx, scope); parser.wrap_object_shared(cx, scope);

View file

@ -391,7 +391,7 @@ impl Node {
let mut node = AbstractNode { let mut node = AbstractNode {
obj: transmute(node), obj: transmute(node),
}; };
let cx = global_script_context().compartment.get().cx.ptr; let cx = global_script_context().js_compartment.cx.ptr;
node::create(cx, &mut node); node::create(cx, &mut node);
node node
} }

View file

@ -2,14 +2,15 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use scripting::script_task::{ControlMsg, ExitMsg, ScriptContext, Timer, global_script_context};
use dom::bindings::utils::WrapperCache; use dom::bindings::utils::WrapperCache;
use dom::bindings::window; use dom::bindings::window;
use dom::event::Event; use dom::event::Event;
use js::jsapi::JSVal; use scripting::script_task::{ControlMsg, ExitMsg, FireTimerMsg, ScriptContext};
use scripting::script_task::{global_script_context};
use util::task::spawn_listener; use util::task::spawn_listener;
use core::comm::{Port, Chan, SharedChan}; use core::comm::{Port, Chan, SharedChan};
use js::jsapi::JSVal;
use std::timer; use std::timer;
use std::uv_global_loop; use std::uv_global_loop;
@ -90,20 +91,21 @@ pub fn Window(script_chan: comm::SharedChan<ControlMsg>,
let win = @mut Window { let win = @mut Window {
wrapper: WrapperCache::new(), wrapper: WrapperCache::new(),
dom_event_chan: dom_event_chan, dom_event_chan: dom_event_chan,
timer_chan: do spawn_listener |timer_port: Port<TimerControlMsg>| { timer_chan: {
do spawn_listener |timer_port: Port<TimerControlMsg>| {
loop { loop {
match timer_port.recv() { match timer_port.recv() {
TimerMessage_Close => break, TimerMessage_Close => break,
TimerMessage_Fire(td) => { TimerMessage_Fire(td) => script_chan.send(FireTimerMsg(td)),
script_chan.send(Timer(td)); TimerMessage_TriggerExit => script_chan.send(ExitMsg),
} }
TimerMessage_TriggerExit => script_chan.send(ExitMsg)
} }
} }
}, },
script_context: script_context script_context: script_context,
}; };
let compartment = global_script_context().compartment.get();
let compartment = global_script_context().js_compartment;
window::create(compartment, win); window::create(compartment, win);
win win
} }

View file

@ -6,7 +6,7 @@ use compositing::CompositorImpl;
use dom::event::Event; use dom::event::Event;
use layout::layout_task::LayoutTask; use layout::layout_task::LayoutTask;
use layout::layout_task; use layout::layout_task;
use scripting::script_task::{ExecuteMsg, ParseMsg, ScriptTask}; use scripting::script_task::{ExecuteMsg, LoadMsg, ScriptTask};
use scripting::script_task; use scripting::script_task;
use util::task::spawn_listener; use util::task::spawn_listener;
@ -55,7 +55,7 @@ impl Engine {
let opts = opts.take(); let opts = opts.take();
let layout_task = LayoutTask(render_task.clone(), image_cache_task.clone(), opts); let layout_task = LayoutTask(render_task.clone(), image_cache_task.clone(), opts);
let script_task = ScriptTask(layout_task.clone(), let script_task = ScriptTask::new(layout_task.clone(),
dom_event_port.take(), dom_event_port.take(),
dom_event_chan.take(), dom_event_chan.take(),
resource_task.clone(), resource_task.clone(),
@ -83,15 +83,15 @@ impl Engine {
match request { match request {
LoadUrlMsg(url) => { LoadUrlMsg(url) => {
if url.path.ends_with(".js") { if url.path.ends_with(".js") {
self.script_task.send(ExecuteMsg(url)) self.script_task.chan.send(ExecuteMsg(url))
} else { } else {
self.script_task.send(ParseMsg(url)) self.script_task.chan.send(LoadMsg(url))
} }
return true return true
} }
ExitMsg(sender) => { ExitMsg(sender) => {
self.script_task.send(script_task::ExitMsg); self.script_task.chan.send(script_task::ExitMsg);
self.layout_task.send(layout_task::ExitMsg); self.layout_task.send(layout_task::ExitMsg);
let (response_port, response_chan) = comm::stream(); let (response_port, response_chan) = comm::stream();

View file

@ -10,53 +10,61 @@ use dom::document::Document;
use dom::event::{Event, ResizeEvent, ReflowEvent}; use dom::event::{Event, ResizeEvent, ReflowEvent};
use dom::node::define_bindings; use dom::node::define_bindings;
use dom::window::Window; use dom::window::Window;
use layout::layout_task::{AddStylesheet, BuildData, BuildMsg, Damage, LayoutTask}; use layout::layout_task::{AddStylesheet, BuildData, BuildMsg, Damage, LayoutQuery};
use layout::layout_task::{MatchSelectorsDamage, NoDamage, ReflowDamage}; use layout::layout_task::{LayoutQueryResponse, LayoutTask, MatchSelectorsDamage, NoDamage};
use layout::layout_task::{QueryMsg, ReflowDamage};
use layout::layout_task; use layout::layout_task;
use core::cast::transmute;
use core::cell::Cell; use core::cell::Cell;
use core::comm::{Port, SharedChan}; use core::comm::{Port, SharedChan};
use core::either;
use core::io::read_whole_file; use core::io::read_whole_file;
use core::local_data; use core::local_data;
use core::pipes::select2i; use core::pipes::select2i;
use core::ptr::null; use core::ptr::null;
use core::task::{SingleThreaded, task}; use core::task::{SingleThreaded, task};
use core::util::replace; use core::util::replace;
use dom; use dom::window::TimerData;
use geom::size::Size2D; use geom::size::Size2D;
use html; use html::hubbub_html_parser;
use js::JSVAL_NULL; use js::JSVAL_NULL;
use js::global::{global_class, debug_fns}; use js::global::{global_class, debug_fns};
use js::glue::bindgen::RUST_JSVAL_TO_OBJECT; use js::glue::bindgen::RUST_JSVAL_TO_OBJECT;
use js::jsapi::JSContext; use js::jsapi::JSContext;
use js::jsapi::bindgen::{JS_CallFunctionValue, JS_GetContextPrivate}; use js::jsapi::bindgen::{JS_CallFunctionValue, JS_GetContextPrivate};
use js::rust::{Compartment, Cx}; use js::rust::{Compartment, Cx};
use jsrt = js::rust::rt; use js;
use servo_net::image_cache_task::ImageCacheTask; use servo_net::image_cache_task::ImageCacheTask;
use servo_net::resource_task::ResourceTask; use servo_net::resource_task::ResourceTask;
use servo_util::tree::TreeNodeRef; use servo_util::tree::TreeNodeRef;
use std::net::url::Url; use std::net::url::Url;
use url_to_str = std::net::url::to_str; use std::net::url;
/// Messages used to control the script task at a high level.
pub enum ControlMsg { pub enum ControlMsg {
ParseMsg(Url), /// Loads a new URL.
LoadMsg(Url),
/// Executes a standalone script.
ExecuteMsg(Url), ExecuteMsg(Url),
Timer(~dom::window::TimerData), /// Fires a JavaScript timeout.
ExitMsg FireTimerMsg(~TimerData),
/// Exits the engine.
ExitMsg,
} }
pub enum PingMsg { /// Encapsulates external communication with the script task.
PongMsg pub struct ScriptTask {
/// The channel used to send control messages to the script task.
chan: SharedChan<ControlMsg>,
} }
pub type ScriptTask = SharedChan<ControlMsg>; impl ScriptTask {
/// Creates a new script task.
pub fn ScriptTask(layout_task: LayoutTask, pub fn new(layout_task: LayoutTask,
dom_event_port: Port<Event>, dom_event_port: Port<Event>,
dom_event_chan: SharedChan<Event>, dom_event_chan: SharedChan<Event>,
resource_task: ResourceTask, resource_task: ResourceTask,
img_cache_task: ImageCacheTask) image_cache_task: ImageCacheTask)
-> ScriptTask { -> ScriptTask {
let (control_port, control_chan) = comm::stream(); let (control_port, control_chan) = comm::stream();
@ -70,107 +78,87 @@ pub fn ScriptTask(layout_task: LayoutTask,
let mut the_task = task(); let mut the_task = task();
the_task.sched_mode(SingleThreaded); the_task.sched_mode(SingleThreaded);
do the_task.spawn { do the_task.spawn {
let script_context = ScriptContext(layout_task.clone(), let script_context = ScriptContext::new(layout_task.clone(),
control_port.take(), control_port.take(),
control_chan_copy.clone(), control_chan_copy.clone(),
resource_task.clone(), resource_task.clone(),
img_cache_task.clone(), image_cache_task.clone(),
dom_event_port.take(), dom_event_port.take(),
dom_event_chan.take()); dom_event_chan.take());
script_context.start(); script_context.start();
} }
return control_chan; ScriptTask {
chan: control_chan
}
}
} }
/// Information for one frame in the browsing context.
pub struct Frame {
document: @mut Document,
window: @mut Window,
url: Url,
}
/// Information for an entire page. Pages are top-level browsing contexts and can contain multiple
/// frames.
///
/// FIXME: Rename to `Page`, following WebKit?
pub struct ScriptContext { pub struct ScriptContext {
/// A handle to the layout task.
layout_task: LayoutTask, layout_task: LayoutTask,
layout_join_port: Option<comm::Port<()>>, /// A handle to the image cache task.
image_cache_task: ImageCacheTask, image_cache_task: ImageCacheTask,
control_port: comm::Port<ControlMsg>, /// A handle to the resource task.
control_chan: comm::SharedChan<ControlMsg>, resource_task: ResourceTask,
event_port: comm::Port<Event>,
event_chan: comm::SharedChan<Event>,
jsrt: jsrt, /// The port that we will use to join layout. If this is `None`, then layout is not currently
cx: @Cx, /// running.
layout_join_port: Option<Port<()>>,
/// The port on which we receive control messages (load URL, exit, etc.)
control_port: Port<ControlMsg>,
/// A channel for us to hand out when we want some other task to be able to send us control
/// messages.
control_chan: SharedChan<ControlMsg>,
/// The port on which we receive DOM events.
event_port: Port<Event>,
/// A channel for us to hand out when we want some other task to be able to send us DOM
/// events.
event_chan: SharedChan<Event>,
/// The JavaScript runtime.
js_runtime: js::rust::rt,
/// The JavaScript context.
js_context: @Cx,
/// The JavaScript compartment.
js_compartment: @mut Compartment,
/// Global static data related to the DOM.
dom_static: GlobalStaticData, dom_static: GlobalStaticData,
document: Option<@mut Document>, /// The outermost frame. This frame contains the document, window, and page URL.
window: Option<@mut Window>, root_frame: Option<Frame>,
doc_url: Option<Url>,
/// The current size of the window, in pixels.
window_size: Size2D<uint>, window_size: Size2D<uint>,
/// What parts of layout are dirty.
resource_task: ResourceTask,
compartment: Option<@mut Compartment>,
// What parts of layout are dirty.
damage: Damage, damage: Damage,
} }
pub fn ScriptContext(layout_task: LayoutTask,
control_port: comm::Port<ControlMsg>,
control_chan: comm::SharedChan<ControlMsg>,
resource_task: ResourceTask,
img_cache_task: ImageCacheTask,
event_port: comm::Port<Event>,
event_chan: comm::SharedChan<Event>)
-> @mut ScriptContext {
let jsrt = jsrt();
let cx = jsrt.cx();
cx.set_default_options_and_version();
cx.set_logging_error_reporter();
let compartment = match cx.new_compartment(global_class) {
Ok(c) => Some(c),
Err(()) => None
};
let script_context = @mut ScriptContext {
layout_task: layout_task,
layout_join_port: None,
image_cache_task: img_cache_task,
control_port: control_port,
control_chan: control_chan,
event_port: event_port,
event_chan: event_chan,
jsrt: jsrt,
cx: cx,
dom_static: GlobalStaticData(),
document: None,
window: None,
doc_url: None,
window_size: Size2D(800u, 600u),
resource_task: resource_task,
compartment: compartment,
damage: MatchSelectorsDamage,
};
cx.set_cx_private(ptr::to_unsafe_ptr(&*script_context) as *());
unsafe {
local_data::local_data_set(global_script_context_key, cast::transmute(script_context));
}
script_context
}
fn global_script_context_key(_: @ScriptContext) {} fn global_script_context_key(_: @ScriptContext) {}
/// Returns this task's script context singleton.
pub fn global_script_context() -> @ScriptContext { pub fn global_script_context() -> @ScriptContext {
unsafe { unsafe {
return local_data::local_data_get(global_script_context_key).get(); local_data::local_data_get(global_script_context_key).get()
} }
} }
pub fn task_from_context(cx: *JSContext) -> *mut ScriptContext { /// Returns the script context from the JS Context.
JS_GetContextPrivate(cx) as *mut ScriptContext ///
/// FIXME: Rename to `script_context_from_js_context`.
pub fn task_from_context(js_context: *JSContext) -> *mut ScriptContext {
JS_GetContextPrivate(js_context) as *mut ScriptContext
} }
#[unsafe_destructor] #[unsafe_destructor]
@ -182,139 +170,215 @@ impl Drop for ScriptContext {
} }
} }
#[allow(non_implicitly_copyable_typarams)] impl ScriptContext {
pub impl ScriptContext { /// Creates a new script context.
fn start(&mut self) { pub fn new(layout_task: LayoutTask,
control_port: Port<ControlMsg>,
control_chan: SharedChan<ControlMsg>,
resource_task: ResourceTask,
img_cache_task: ImageCacheTask,
event_port: Port<Event>,
event_chan: SharedChan<Event>)
-> @mut ScriptContext {
let js_runtime = js::rust::rt();
let js_context = js_runtime.cx();
js_context.set_default_options_and_version();
js_context.set_logging_error_reporter();
let compartment = match js_context.new_compartment(global_class) {
Ok(c) => c,
Err(()) => fail!("Failed to create a compartment"),
};
let script_context = @mut ScriptContext {
layout_task: layout_task,
image_cache_task: img_cache_task,
resource_task: resource_task,
layout_join_port: None,
control_port: control_port,
control_chan: control_chan,
event_port: event_port,
event_chan: event_chan,
js_runtime: js_runtime,
js_context: js_context,
js_compartment: compartment,
dom_static: GlobalStaticData(),
root_frame: None,
window_size: Size2D(800, 600),
damage: MatchSelectorsDamage,
};
let script_context_ptr: *ScriptContext = &*script_context;
js_context.set_cx_private(script_context_ptr as *());
unsafe {
local_data::local_data_set(global_script_context_key, transmute(script_context))
}
script_context
}
/// Starts the script task. After calling this method, the script task will loop receiving
/// messages on its port.
pub fn start(&mut self) {
while self.handle_msg() { while self.handle_msg() {
// Go on... // Go on...
} }
} }
/// Handles an incoming control message.
fn handle_msg(&mut self) -> bool { fn handle_msg(&mut self) -> bool {
match select2i(&mut self.control_port, &mut self.event_port) { match select2i(&mut self.control_port, &mut self.event_port) {
either::Left(*) => { Left(*) => {
let msg = self.control_port.recv(); let msg = self.control_port.recv();
self.handle_control_msg(msg) self.handle_control_msg(msg)
} }
either::Right(*) => { Right(*) => {
let ev = self.event_port.recv(); let ev = self.event_port.recv();
self.handle_event(ev) self.handle_event(ev);
true
} }
} }
} }
/// Handles a control message from the compositor/front-end. Returns true if the task is to
/// continue and false otherwise.
fn handle_control_msg(&mut self, control_msg: ControlMsg) -> bool { fn handle_control_msg(&mut self, control_msg: ControlMsg) -> bool {
match control_msg { match control_msg {
ParseMsg(url) => { LoadMsg(url) => {
debug!("script: Received url `%s` to parse", url_to_str(&url)); debug!("script: Received url `%s` to load", url::to_str(&url));
self.load(url);
define_bindings(self.compartment.get()); true
// Note: we can parse the next document in parallel
// with any previous documents.
let result = html::hubbub_html_parser::parse_html(copy url,
self.resource_task.clone(),
self.image_cache_task.clone());
let root = result.root;
// Send stylesheets over to layout
// FIXME: Need these should be streamed to layout as they are parsed
// and do not need to stop here in the script task
loop {
match result.style_port.recv() {
Some(sheet) => {
self.layout_task.send(AddStylesheet(sheet));
}
None => break
}
} }
let js_scripts = result.js_port.recv(); FireTimerMsg(timer_data) => {
debug!("js_scripts: %?", js_scripts); let this_value = if timer_data.args.len() > 0 {
RUST_JSVAL_TO_OBJECT(timer_data.args[0])
let window = Window(self.control_chan.clone(),
self.event_chan.clone(),
ptr::to_mut_unsafe_ptr(&mut *self)); //FIXME store this safely
let document = Document(root, Some(window));
do root.with_mut_base |base| {
base.add_to_doc(document);
}
self.damage.add(MatchSelectorsDamage);
self.relayout(document, &url);
self.document = Some(document);
self.window = Some(window);
self.doc_url = Some(url);
let compartment = self.compartment.expect(~"TODO error checking");
compartment.define_functions(debug_fns);
do vec::consume(js_scripts) |_i, bytes| {
self.cx.evaluate_script(compartment.global_obj, bytes, ~"???", 1u);
}
return true;
}
Timer(timerData) => {
let compartment = self.compartment.expect(~"TODO error checking");
let thisValue = if timerData.args.len() > 0 {
RUST_JSVAL_TO_OBJECT(timerData.args[0])
} else { } else {
compartment.global_obj.ptr self.js_compartment.global_obj.ptr
}; };
let rval = JSVAL_NULL; let rval = JSVAL_NULL;
//TODO: support extra args. requires passing a *JSVal argv // TODO: Support extra arguments. This requires passing a `*JSVal` array as `argv`.
JS_CallFunctionValue(self.cx.ptr, thisValue, timerData.funval, JS_CallFunctionValue(self.js_context.ptr,
0, null(), ptr::to_unsafe_ptr(&rval)); this_value,
self.relayout(self.document.get(), &(copy self.doc_url).get()); timer_data.funval,
return true; 0,
null(),
&rval);
self.relayout();
true
} }
ExecuteMsg(url) => { ExecuteMsg(url) => {
debug!("script: Received url `%s` to execute", url_to_str(&url)); debug!("script: Received url `%s` to execute", url::to_str(&url));
match read_whole_file(&Path(url.path)) { match read_whole_file(&Path(url.path)) {
Err(msg) => { Err(msg) => println(fmt!("Error opening %s: %s", url::to_str(&url), msg)),
println(fmt!("Error opening %s: %s", url_to_str(&url), msg));
}
Ok(bytes) => { Ok(bytes) => {
let compartment = self.compartment.expect(~"TODO error checking"); self.js_compartment.define_functions(debug_fns);
compartment.define_functions(debug_fns); let _ = self.js_context.evaluate_script(self.js_compartment.global_obj,
self.cx.evaluate_script(compartment.global_obj, bytes, copy url.path, 1u); bytes,
copy url.path,
1);
} }
} }
return true;
true
} }
ExitMsg => { ExitMsg => {
self.layout_task.send(layout_task::ExitMsg); self.layout_task.send(layout_task::ExitMsg);
return false; false
} }
} }
} }
/** /// The entry point to document loading. Defines bindings, sets up the window and document
Sends a ping to layout and waits for the response (i.e., it has finished any /// objects, parses HTML and CSS, and kicks off initial layout.
pending layout request messages). fn load(&mut self, url: Url) {
*/ // Define the script DOM bindings.
define_bindings(self.js_compartment);
// Parse HTML.
//
// Note: We can parse the next document in parallel with any previous documents.
let html_parsing_result = hubbub_html_parser::parse_html(copy url,
self.resource_task.clone(),
self.image_cache_task.clone());
let root_node = html_parsing_result.root;
// Send style sheets over to layout.
//
// FIXME: These should be streamed to layout as they're parsed. We don't need to stop here
// in the script task.
loop {
match html_parsing_result.style_port.recv() {
Some(sheet) => self.layout_task.send(AddStylesheet(sheet)),
None => break,
}
}
// Receive the JavaScript scripts.
let js_scripts = html_parsing_result.js_port.recv();
debug!("js_scripts: %?", js_scripts);
// Create the window and document objects.
let window = Window(self.control_chan.clone(), self.event_chan.clone(), &mut *self);
let document = Document(root_node, Some(window));
// Tie the root into the document.
do root_node.with_mut_base |base| {
base.add_to_doc(document)
}
// Create the root frame.
self.root_frame = Some(Frame {
document: document,
window: window,
url: url
});
// Perform the initial reflow.
self.damage.add(MatchSelectorsDamage);
self.relayout();
// Define debug functions.
self.js_compartment.define_functions(debug_fns);
// Evaluate every script in the document.
do vec::consume(js_scripts) |_, bytes| {
let _ = self.js_context.evaluate_script(self.js_compartment.global_obj,
bytes,
~"???",
1);
}
}
/// Sends a ping to layout and waits for the response. The response will arrive when the
/// layout task has finished any pending request messages.
fn join_layout(&mut self) { fn join_layout(&mut self) {
if self.layout_join_port.is_some() { if self.layout_join_port.is_some() {
let join_port = replace(&mut self.layout_join_port, None); let join_port = replace(&mut self.layout_join_port, None);
match join_port { match join_port {
Some(ref join_port) => { Some(ref join_port) => {
if !join_port.peek() { if !join_port.peek() {
warn!("script: waiting on layout"); info!("script: waiting on layout");
} }
join_port.recv(); join_port.recv();
debug!("script: layout joined");
debug!("script: layout joined")
} }
None => fail!(~"reader forked but no join port?") None => fail!(~"reader forked but no join port?"),
} }
} }
} }
@ -322,7 +386,9 @@ pub impl ScriptContext {
/// This method will wait until the layout task has completed its current action, join the /// This method will wait until the layout task has completed its current action, join the
/// layout task, and then request a new layout run. It won't wait for the new layout /// layout task, and then request a new layout run. It won't wait for the new layout
/// computation to finish. /// computation to finish.
fn relayout(&mut self, document: &Document, doc_url: &Url) { ///
/// This function fails if there is no root frame.
fn relayout(&mut self) {
debug!("script: performing relayout"); debug!("script: performing relayout");
// Now, join the layout so that they will see the latest changes we have made. // Now, join the layout so that they will see the latest changes we have made.
@ -332,67 +398,64 @@ pub impl ScriptContext {
let (join_port, join_chan) = comm::stream(); let (join_port, join_chan) = comm::stream();
self.layout_join_port = Some(join_port); self.layout_join_port = Some(join_port);
match self.root_frame {
None => fail!(~"Tried to relayout with no root frame!"),
Some(ref root_frame) => {
// Send new document and relevant styles to layout. // Send new document and relevant styles to layout.
let data = ~BuildData { let data = ~BuildData {
node: document.root, node: root_frame.document.root,
url: copy *doc_url, url: copy root_frame.url,
dom_event_chan: self.event_chan.clone(), dom_event_chan: self.event_chan.clone(),
window_size: self.window_size, window_size: self.window_size,
script_join_chan: join_chan, script_join_chan: join_chan,
damage: replace(&mut self.damage, NoDamage), damage: replace(&mut self.damage, NoDamage),
}; };
self.layout_task.send(BuildMsg(data)); self.layout_task.send(BuildMsg(data))
}
debug!("script: layout forked");
} }
fn query_layout(&mut self, query: layout_task::LayoutQuery) debug!("script: layout forked")
-> layout_task::LayoutQueryResponse { }
//self.relayout(self.document.get(), &(copy self.doc_url).get());
/// Sends the given query to layout.
pub fn query_layout(&mut self, query: LayoutQuery) -> LayoutQueryResponse {
//self.relayout();
self.join_layout(); self.join_layout();
let (response_port, response_chan) = comm::stream(); let (response_port, response_chan) = comm::stream();
self.layout_task.send(layout_task::QueryMsg(query, response_chan)); self.layout_task.send(QueryMsg(query, response_chan));
return response_port.recv() response_port.recv()
} }
/** /// This is the main entry point for receiving and dispatching DOM events.
This is the main entry point for receiving and dispatching DOM events. ///
*/ /// TODO: Actually perform DOM event dispatch.
// TODO: actually perform DOM event dispatch. fn handle_event(&mut self, event: Event) {
fn handle_event(&mut self, event: Event) -> bool {
match event { match event {
ResizeEvent(new_width, new_height, response_chan) => { ResizeEvent(new_width, new_height, response_chan) => {
debug!("script got resize event: %u, %u", new_width, new_height); debug!("script got resize event: %u, %u", new_width, new_height);
self.damage.add(ReflowDamage); self.damage.add(ReflowDamage);
self.window_size = Size2D(new_width, new_height); self.window_size = Size2D(new_width, new_height);
match copy self.document {
None => { if self.root_frame.is_some() {
// Nothing to do. self.relayout()
} }
Some(document) => {
assert!(self.doc_url.is_some()); response_chan.send(())
self.relayout(document, &(copy self.doc_url).get());
}
}
response_chan.send(());
return true;
} }
ReflowEvent => { ReflowEvent => {
debug!("script got reflow event"); debug!("script got reflow event");
self.damage.add(MatchSelectorsDamage); self.damage.add(MatchSelectorsDamage);
match /*bad*/ copy self.document {
None => { if self.root_frame.is_some() {
// Nothing to do. self.relayout()
} }
Some(document) => {
assert!(self.doc_url.is_some());
self.relayout(document, &(copy self.doc_url).get());
}
}
return true;
} }
} }
} }
} }