From 2ba5dfbfe3b843e49fd8a55882413c8861d1b466 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Thu, 16 May 2013 14:33:22 -0700 Subject: [PATCH] Refactor the script task to conform to current coding style --- .../servo/dom/bindings/clientrect.rs | 6 +- .../servo/dom/bindings/clientrectlist.rs | 12 +- .../servo/dom/bindings/codegen/CodegenRust.py | 2 +- src/components/servo/dom/bindings/document.rs | 5 +- src/components/servo/dom/bindings/event.rs | 8 +- .../servo/dom/bindings/eventtarget.rs | 13 +- .../servo/dom/bindings/htmlcollection.rs | 12 +- src/components/servo/dom/bindings/utils.rs | 3 +- src/components/servo/dom/document.rs | 4 +- src/components/servo/dom/domparser.rs | 2 +- src/components/servo/dom/node.rs | 2 +- src/components/servo/dom/window.rs | 24 +- src/components/servo/engine.rs | 18 +- src/components/servo/scripting/script_task.rs | 569 ++++++++++-------- 14 files changed, 381 insertions(+), 299 deletions(-) diff --git a/src/components/servo/dom/bindings/clientrect.rs b/src/components/servo/dom/bindings/clientrect.rs index 836da7a63a6..ca7317de083 100644 --- a/src/components/servo/dom/bindings/clientrect.rs +++ b/src/components/servo/dom/bindings/clientrect.rs @@ -12,8 +12,8 @@ use scripting::script_task::{task_from_context, global_script_context}; pub impl ClientRect { pub fn init_wrapper(@mut self) { let script_context = global_script_context(); - let cx = script_context.compartment.get().cx.ptr; - let owner = script_context.window.get(); + let cx = script_context.js_compartment.cx.ptr; + let owner = script_context.root_frame.get_ref().window; let cache = owner.get_wrappercache(); let scope = cache.get_wrapper(); self.wrap_object_shared(cx, scope); @@ -37,7 +37,7 @@ impl BindingObject for ClientRect { fn GetParentObject(&self, cx: *JSContext) -> @mut CacheableWrapper { let script_context = task_from_context(cx); unsafe { - (*script_context).window.get() as @mut CacheableWrapper + (*script_context).root_frame.get_ref().window as @mut CacheableWrapper } } } diff --git a/src/components/servo/dom/bindings/clientrectlist.rs b/src/components/servo/dom/bindings/clientrectlist.rs index d13fa7c7c2b..783dc78267d 100644 --- a/src/components/servo/dom/bindings/clientrectlist.rs +++ b/src/components/servo/dom/bindings/clientrectlist.rs @@ -11,8 +11,8 @@ use scripting::script_task::{task_from_context, global_script_context}; pub impl ClientRectList { fn init_wrapper(@mut self) { let script_context = global_script_context(); - let cx = script_context.compartment.get().cx.ptr; - let owner = script_context.window.get(); + let cx = script_context.js_compartment.cx.ptr; + let owner = script_context.root_frame.get_ref().window; let cache = owner.get_wrappercache(); let scope = cache.get_wrapper(); self.wrap_object_shared(cx, scope); @@ -21,7 +21,9 @@ pub impl ClientRectList { impl CacheableWrapper for ClientRectList { 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 { @@ -33,6 +35,8 @@ impl CacheableWrapper for ClientRectList { impl BindingObject for ClientRectList { fn GetParentObject(&self, cx: *JSContext) -> @mut CacheableWrapper { 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 + } } } diff --git a/src/components/servo/dom/bindings/codegen/CodegenRust.py b/src/components/servo/dom/bindings/codegen/CodegenRust.py index 01257749017..624018c394e 100644 --- a/src/components/servo/dom/bindings/codegen/CodegenRust.py +++ b/src/components/servo/dom/bindings/codegen/CodegenRust.py @@ -3615,7 +3615,7 @@ class CGClassConstructHook(CGAbstractExternMethod): // or through unwrapping a slot or something). We'll punt and get the Window // from the context for now. 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(); """ preArgs = ["global"] diff --git a/src/components/servo/dom/bindings/document.rs b/src/components/servo/dom/bindings/document.rs index 11d2328b579..36029e8d5b8 100644 --- a/src/components/servo/dom/bindings/document.rs +++ b/src/components/servo/dom/bindings/document.rs @@ -146,6 +146,9 @@ impl CacheableWrapper for Document { fn wrap_object_shared(@mut self, cx: *JSContext, _scope: *JSObject) -> *JSObject { let script_context = task_from_context(cx); - unsafe { create((*script_context).compartment.get(), self) } + unsafe { + create((*script_context).js_compartment, self) + } } } + diff --git a/src/components/servo/dom/bindings/event.rs b/src/components/servo/dom/bindings/event.rs index f4a7d159c71..1463ffe7ea4 100644 --- a/src/components/servo/dom/bindings/event.rs +++ b/src/components/servo/dom/bindings/event.rs @@ -12,8 +12,8 @@ use scripting::script_task::{task_from_context, global_script_context}; pub impl Event_ { pub fn init_wrapper(@mut self) { let script_context = global_script_context(); - let cx = script_context.compartment.get().cx.ptr; - let owner = script_context.window.get(); + let cx = script_context.js_compartment.cx.ptr; + let owner = script_context.root_frame.get_ref().window; let cache = owner.get_wrappercache(); let scope = cache.get_wrapper(); self.wrap_object_shared(cx, scope); @@ -34,7 +34,9 @@ impl CacheableWrapper for Event_ { impl BindingObject for Event_ { fn GetParentObject(&self, cx: *JSContext) -> @mut CacheableWrapper { 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 + } } } diff --git a/src/components/servo/dom/bindings/eventtarget.rs b/src/components/servo/dom/bindings/eventtarget.rs index 1e3e1bb6de7..2b721f92d3e 100644 --- a/src/components/servo/dom/bindings/eventtarget.rs +++ b/src/components/servo/dom/bindings/eventtarget.rs @@ -5,6 +5,7 @@ use dom::bindings::codegen::EventTargetBinding; use dom::bindings::utils::{CacheableWrapper, WrapperCache, BindingObject, DerivedWrapper}; use dom::eventtarget::EventTarget; + use js::glue::bindgen::RUST_OBJECT_TO_JSVAL; use js::jsapi::{JSObject, JSContext, JSVal}; 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 fn init_wrapper(@mut self) { let script_context = global_script_context(); - let cx = script_context.compartment.get().cx.ptr; - let owner = script_context.window.get(); + let cx = script_context.js_compartment.cx.ptr; + let owner = script_context.root_frame.get_ref().window; let cache = owner.get_wrappercache(); let scope = cache.get_wrapper(); self.wrap_object_shared(cx, scope); @@ -34,7 +35,9 @@ impl CacheableWrapper for EventTarget { impl BindingObject for EventTarget { fn GetParentObject(&self, cx: *JSContext) -> @mut CacheableWrapper { 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() { return 0; } else { - unsafe { *vp = RUST_OBJECT_TO_JSVAL(obj) }; + unsafe { + *vp = RUST_OBJECT_TO_JSVAL(obj) + }; return 1; } } diff --git a/src/components/servo/dom/bindings/htmlcollection.rs b/src/components/servo/dom/bindings/htmlcollection.rs index 7ed9b251498..11e3f75e0d0 100644 --- a/src/components/servo/dom/bindings/htmlcollection.rs +++ b/src/components/servo/dom/bindings/htmlcollection.rs @@ -11,8 +11,8 @@ use scripting::script_task::{task_from_context, global_script_context}; pub impl HTMLCollection { fn init_wrapper(@mut self) { let script_context = global_script_context(); - let cx = script_context.compartment.get().cx.ptr; - let owner = script_context.window.get(); + let cx = script_context.js_compartment.cx.ptr; + let owner = script_context.root_frame.get_ref().window; let cache = owner.get_wrappercache(); let scope = cache.get_wrapper(); self.wrap_object_shared(cx, scope); @@ -22,13 +22,17 @@ pub impl HTMLCollection { impl BindingObject for HTMLCollection { fn GetParentObject(&self, cx: *JSContext) -> @mut CacheableWrapper { 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 { 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 { diff --git a/src/components/servo/dom/bindings/utils.rs b/src/components/servo/dom/bindings/utils.rs index 72aefed1254..a27207ef32d 100644 --- a/src/components/servo/dom/bindings/utils.rs +++ b/src/components/servo/dom/bindings/utils.rs @@ -164,8 +164,7 @@ pub unsafe fn domstring_to_jsval(cx: *JSContext, string: &DOMString) -> JSVal { pub fn get_compartment(cx: *JSContext) -> @mut Compartment { unsafe { let script_context = task_from_context(cx); - let compartment = (*script_context).compartment.expect(~"Should always have compartment \ - when executing JS code"); + let compartment = (*script_context).js_compartment; assert!(cx == compartment.cx.ptr); compartment } diff --git a/src/components/servo/dom/document.rs b/src/components/servo/dom/document.rs index ece141fd903..932cecbb8a6 100644 --- a/src/components/servo/dom/document.rs +++ b/src/components/servo/dom/document.rs @@ -25,7 +25,7 @@ pub fn Document(root: AbstractNode, window: Option<@mut Window>) -> @mut Documen wrapper: WrapperCache::new(), window: window }; - let compartment = global_script_context().compartment.get(); + let compartment = global_script_context().js_compartment; do root.with_base |base| { assert!(base.wrapper.get_wrapper().is_not_null()); let rootable = base.wrapper.get_rootable(); @@ -38,7 +38,7 @@ pub fn Document(root: AbstractNode, window: Option<@mut Window>) -> @mut Documen #[unsafe_destructor] impl Drop for Document { fn finalize(&self) { - let compartment = global_script_context().compartment.get(); + let compartment = global_script_context().js_compartment; do self.root.with_base |base| { assert!(base.wrapper.get_wrapper().is_not_null()); let rootable = base.wrapper.get_rootable(); diff --git a/src/components/servo/dom/domparser.rs b/src/components/servo/dom/domparser.rs index b92ebfa6ec8..8e8ed345a08 100644 --- a/src/components/servo/dom/domparser.rs +++ b/src/components/servo/dom/domparser.rs @@ -22,7 +22,7 @@ impl DOMParser { 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 scope = cache.get_wrapper(); parser.wrap_object_shared(cx, scope); diff --git a/src/components/servo/dom/node.rs b/src/components/servo/dom/node.rs index f3ad34f9dc3..5ae3656e75e 100644 --- a/src/components/servo/dom/node.rs +++ b/src/components/servo/dom/node.rs @@ -391,7 +391,7 @@ impl Node { let mut node = AbstractNode { 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 } diff --git a/src/components/servo/dom/window.rs b/src/components/servo/dom/window.rs index 8ed3549d2de..4651d0f0c9f 100644 --- a/src/components/servo/dom/window.rs +++ b/src/components/servo/dom/window.rs @@ -2,14 +2,15 @@ * 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 scripting::script_task::{ControlMsg, ExitMsg, ScriptContext, Timer, global_script_context}; use dom::bindings::utils::WrapperCache; use dom::bindings::window; 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 core::comm::{Port, Chan, SharedChan}; +use js::jsapi::JSVal; use std::timer; use std::uv_global_loop; @@ -90,20 +91,21 @@ pub fn Window(script_chan: comm::SharedChan, let win = @mut Window { wrapper: WrapperCache::new(), dom_event_chan: dom_event_chan, - timer_chan: do spawn_listener |timer_port: Port| { - loop { - match timer_port.recv() { - TimerMessage_Close => break, - TimerMessage_Fire(td) => { - script_chan.send(Timer(td)); + timer_chan: { + do spawn_listener |timer_port: Port| { + loop { + match timer_port.recv() { + TimerMessage_Close => break, + TimerMessage_Fire(td) => script_chan.send(FireTimerMsg(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); win } diff --git a/src/components/servo/engine.rs b/src/components/servo/engine.rs index f87604ca454..341231736e6 100644 --- a/src/components/servo/engine.rs +++ b/src/components/servo/engine.rs @@ -6,7 +6,7 @@ use compositing::CompositorImpl; use dom::event::Event; use layout::layout_task::LayoutTask; use layout::layout_task; -use scripting::script_task::{ExecuteMsg, ParseMsg, ScriptTask}; +use scripting::script_task::{ExecuteMsg, LoadMsg, ScriptTask}; use scripting::script_task; use util::task::spawn_listener; @@ -55,11 +55,11 @@ impl Engine { let opts = opts.take(); let layout_task = LayoutTask(render_task.clone(), image_cache_task.clone(), opts); - let script_task = ScriptTask(layout_task.clone(), - dom_event_port.take(), - dom_event_chan.take(), - resource_task.clone(), - image_cache_task.clone()); + let script_task = ScriptTask::new(layout_task.clone(), + dom_event_port.take(), + dom_event_chan.take(), + resource_task.clone(), + image_cache_task.clone()); Engine { request_port: request, @@ -83,15 +83,15 @@ impl Engine { match request { LoadUrlMsg(url) => { if url.path.ends_with(".js") { - self.script_task.send(ExecuteMsg(url)) + self.script_task.chan.send(ExecuteMsg(url)) } else { - self.script_task.send(ParseMsg(url)) + self.script_task.chan.send(LoadMsg(url)) } return true } ExitMsg(sender) => { - self.script_task.send(script_task::ExitMsg); + self.script_task.chan.send(script_task::ExitMsg); self.layout_task.send(layout_task::ExitMsg); let (response_port, response_chan) = comm::stream(); diff --git a/src/components/servo/scripting/script_task.rs b/src/components/servo/scripting/script_task.rs index add4e8f3472..eeb83358b0c 100644 --- a/src/components/servo/scripting/script_task.rs +++ b/src/components/servo/scripting/script_task.rs @@ -10,167 +10,155 @@ use dom::document::Document; use dom::event::{Event, ResizeEvent, ReflowEvent}; use dom::node::define_bindings; use dom::window::Window; -use layout::layout_task::{AddStylesheet, BuildData, BuildMsg, Damage, LayoutTask}; -use layout::layout_task::{MatchSelectorsDamage, NoDamage, ReflowDamage}; +use layout::layout_task::{AddStylesheet, BuildData, BuildMsg, Damage, LayoutQuery}; +use layout::layout_task::{LayoutQueryResponse, LayoutTask, MatchSelectorsDamage, NoDamage}; +use layout::layout_task::{QueryMsg, ReflowDamage}; use layout::layout_task; +use core::cast::transmute; use core::cell::Cell; use core::comm::{Port, SharedChan}; -use core::either; use core::io::read_whole_file; use core::local_data; use core::pipes::select2i; use core::ptr::null; use core::task::{SingleThreaded, task}; use core::util::replace; -use dom; +use dom::window::TimerData; use geom::size::Size2D; -use html; +use html::hubbub_html_parser; use js::JSVAL_NULL; use js::global::{global_class, debug_fns}; use js::glue::bindgen::RUST_JSVAL_TO_OBJECT; use js::jsapi::JSContext; use js::jsapi::bindgen::{JS_CallFunctionValue, JS_GetContextPrivate}; use js::rust::{Compartment, Cx}; -use jsrt = js::rust::rt; +use js; use servo_net::image_cache_task::ImageCacheTask; use servo_net::resource_task::ResourceTask; use servo_util::tree::TreeNodeRef; 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 { - ParseMsg(Url), + /// Loads a new URL. + LoadMsg(Url), + /// Executes a standalone script. ExecuteMsg(Url), - Timer(~dom::window::TimerData), - ExitMsg + /// Fires a JavaScript timeout. + FireTimerMsg(~TimerData), + /// Exits the engine. + ExitMsg, } -pub enum PingMsg { - PongMsg +/// Encapsulates external communication with the script task. +pub struct ScriptTask { + /// The channel used to send control messages to the script task. + chan: SharedChan, } -pub type ScriptTask = SharedChan; +impl ScriptTask { + /// Creates a new script task. + pub fn new(layout_task: LayoutTask, + dom_event_port: Port, + dom_event_chan: SharedChan, + resource_task: ResourceTask, + image_cache_task: ImageCacheTask) + -> ScriptTask { + let (control_port, control_chan) = comm::stream(); -pub fn ScriptTask(layout_task: LayoutTask, - dom_event_port: Port, - dom_event_chan: SharedChan, - resource_task: ResourceTask, - img_cache_task: ImageCacheTask) - -> ScriptTask { - let (control_port, control_chan) = comm::stream(); + let control_chan = SharedChan::new(control_chan); + let control_chan_copy = control_chan.clone(); + let control_port = Cell(control_port); + let dom_event_port = Cell(dom_event_port); + let dom_event_chan = Cell(dom_event_chan); - let control_chan = SharedChan::new(control_chan); - let control_chan_copy = control_chan.clone(); - let control_port = Cell(control_port); - let dom_event_port = Cell(dom_event_port); - let dom_event_chan = Cell(dom_event_chan); + // FIXME: rust#6399 + let mut the_task = task(); + the_task.sched_mode(SingleThreaded); + do the_task.spawn { + let script_context = ScriptContext::new(layout_task.clone(), + control_port.take(), + control_chan_copy.clone(), + resource_task.clone(), + image_cache_task.clone(), + dom_event_port.take(), + dom_event_chan.take()); + script_context.start(); + } - // FIXME: rust#6399 - let mut the_task = task(); - the_task.sched_mode(SingleThreaded); - do the_task.spawn { - let script_context = ScriptContext(layout_task.clone(), - control_port.take(), - control_chan_copy.clone(), - resource_task.clone(), - img_cache_task.clone(), - dom_event_port.take(), - dom_event_chan.take()); - script_context.start(); + ScriptTask { + chan: control_chan + } } - - return 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 { + /// A handle to the layout task. layout_task: LayoutTask, - layout_join_port: Option>, - + /// A handle to the image cache task. image_cache_task: ImageCacheTask, - control_port: comm::Port, - control_chan: comm::SharedChan, - event_port: comm::Port, - event_chan: comm::SharedChan, - - jsrt: jsrt, - cx: @Cx, - dom_static: GlobalStaticData, - - document: Option<@mut Document>, - window: Option<@mut Window>, - doc_url: Option, - window_size: Size2D, - + /// A handle to the resource task. resource_task: ResourceTask, - compartment: Option<@mut Compartment>, + /// The port that we will use to join layout. If this is `None`, then layout is not currently + /// running. + layout_join_port: Option>, + /// The port on which we receive control messages (load URL, exit, etc.) + control_port: Port, + /// A channel for us to hand out when we want some other task to be able to send us control + /// messages. + control_chan: SharedChan, + /// The port on which we receive DOM events. + event_port: Port, + /// A channel for us to hand out when we want some other task to be able to send us DOM + /// events. + event_chan: SharedChan, - // What parts of layout are dirty. + /// 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, + + /// The outermost frame. This frame contains the document, window, and page URL. + root_frame: Option, + + /// The current size of the window, in pixels. + window_size: Size2D, + /// What parts of layout are dirty. damage: Damage, } -pub fn ScriptContext(layout_task: LayoutTask, - control_port: comm::Port, - control_chan: comm::SharedChan, - resource_task: ResourceTask, - img_cache_task: ImageCacheTask, - event_port: comm::Port, - event_chan: comm::SharedChan) - -> @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) {} +/// Returns this task's script context singleton. pub fn global_script_context() -> @ScriptContext { 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 { - JS_GetContextPrivate(cx) as *mut ScriptContext +/// Returns the script context from the JS Context. +/// +/// 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] @@ -182,139 +170,215 @@ impl Drop for ScriptContext { } } -#[allow(non_implicitly_copyable_typarams)] -pub impl ScriptContext { - fn start(&mut self) { +impl ScriptContext { + /// Creates a new script context. + pub fn new(layout_task: LayoutTask, + control_port: Port, + control_chan: SharedChan, + resource_task: ResourceTask, + img_cache_task: ImageCacheTask, + event_port: Port, + event_chan: SharedChan) + -> @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() { // Go on... } } + /// Handles an incoming control message. fn handle_msg(&mut self) -> bool { match select2i(&mut self.control_port, &mut self.event_port) { - either::Left(*) => { + Left(*) => { let msg = self.control_port.recv(); self.handle_control_msg(msg) } - either::Right(*) => { + Right(*) => { 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 { match control_msg { - ParseMsg(url) => { - debug!("script: Received url `%s` to parse", url_to_str(&url)); - - define_bindings(self.compartment.get()); - - // 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(); - debug!("js_scripts: %?", js_scripts); - - 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); + LoadMsg(url) => { + debug!("script: Received url `%s` to load", url::to_str(&url)); + self.load(url); + true } - self.damage.add(MatchSelectorsDamage); - self.relayout(document, &url); + FireTimerMsg(timer_data) => { + let this_value = if timer_data.args.len() > 0 { + RUST_JSVAL_TO_OBJECT(timer_data.args[0]) + } else { + self.js_compartment.global_obj.ptr + }; - self.document = Some(document); - self.window = Some(window); - self.doc_url = Some(url); + let rval = JSVAL_NULL; + // TODO: Support extra arguments. This requires passing a `*JSVal` array as `argv`. + JS_CallFunctionValue(self.js_context.ptr, + this_value, + timer_data.funval, + 0, + null(), + &rval); - 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); + self.relayout(); + true } - 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 { - 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::to_unsafe_ptr(&rval)); - self.relayout(self.document.get(), &(copy self.doc_url).get()); - return true; - } + ExecuteMsg(url) => { + debug!("script: Received url `%s` to execute", url::to_str(&url)); + match read_whole_file(&Path(url.path)) { + Err(msg) => println(fmt!("Error opening %s: %s", url::to_str(&url), msg)), + Ok(bytes) => { + self.js_compartment.define_functions(debug_fns); + let _ = self.js_context.evaluate_script(self.js_compartment.global_obj, + bytes, + copy url.path, + 1); + } + } - ExecuteMsg(url) => { - debug!("script: Received url `%s` to execute", url_to_str(&url)); - - match read_whole_file(&Path(url.path)) { - Err(msg) => { - println(fmt!("Error opening %s: %s", url_to_str(&url), msg)); - } - Ok(bytes) => { - let compartment = self.compartment.expect(~"TODO error checking"); - compartment.define_functions(debug_fns); - self.cx.evaluate_script(compartment.global_obj, bytes, copy url.path, 1u); - } + true } - return true; - } - ExitMsg => { - self.layout_task.send(layout_task::ExitMsg); - return false; - } + ExitMsg => { + self.layout_task.send(layout_task::ExitMsg); + false + } } } - /** - Sends a ping to layout and waits for the response (i.e., it has finished any - pending layout request messages). - */ + /// The entry point to document loading. Defines bindings, sets up the window and document + /// objects, parses HTML and CSS, and kicks off initial layout. + 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) { if self.layout_join_port.is_some() { let join_port = replace(&mut self.layout_join_port, None); match join_port { Some(ref join_port) => { if !join_port.peek() { - warn!("script: waiting on layout"); + info!("script: waiting on layout"); } + 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 /// layout task, and then request a new layout run. It won't wait for the new layout /// 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"); // 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(); self.layout_join_port = Some(join_port); - // Send new document and relevant styles to layout. - let data = ~BuildData { - node: document.root, - url: copy *doc_url, - dom_event_chan: self.event_chan.clone(), - window_size: self.window_size, - script_join_chan: join_chan, - damage: replace(&mut self.damage, NoDamage), - }; + 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. + let data = ~BuildData { + node: root_frame.document.root, + url: copy root_frame.url, + dom_event_chan: self.event_chan.clone(), + window_size: self.window_size, + script_join_chan: join_chan, + damage: replace(&mut self.damage, NoDamage), + }; - self.layout_task.send(BuildMsg(data)); + self.layout_task.send(BuildMsg(data)) + } + } - debug!("script: layout forked"); + debug!("script: layout forked") } - fn query_layout(&mut self, query: layout_task::LayoutQuery) - -> 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(); let (response_port, response_chan) = comm::stream(); - self.layout_task.send(layout_task::QueryMsg(query, response_chan)); - return response_port.recv() + self.layout_task.send(QueryMsg(query, response_chan)); + response_port.recv() } - /** - This is the main entry point for receiving and dispatching DOM events. - */ - // TODO: actually perform DOM event dispatch. - fn handle_event(&mut self, event: Event) -> bool { + /// This is the main entry point for receiving and dispatching DOM events. + /// + /// TODO: Actually perform DOM event dispatch. + fn handle_event(&mut self, event: Event) { match event { - ResizeEvent(new_width, new_height, response_chan) => { - debug!("script got resize event: %u, %u", new_width, new_height); - self.damage.add(ReflowDamage); - self.window_size = Size2D(new_width, new_height); - match copy self.document { - None => { - // Nothing to do. + ResizeEvent(new_width, new_height, response_chan) => { + debug!("script got resize event: %u, %u", new_width, new_height); + + self.damage.add(ReflowDamage); + self.window_size = Size2D(new_width, new_height); + + if self.root_frame.is_some() { + self.relayout() } - Some(document) => { - assert!(self.doc_url.is_some()); - self.relayout(document, &(copy self.doc_url).get()); + + response_chan.send(()) + } + + ReflowEvent => { + debug!("script got reflow event"); + + self.damage.add(MatchSelectorsDamage); + + if self.root_frame.is_some() { + self.relayout() } } - response_chan.send(()); - return true; - } - ReflowEvent => { - debug!("script got reflow event"); - self.damage.add(MatchSelectorsDamage); - match /*bad*/ copy self.document { - None => { - // Nothing to do. - } - Some(document) => { - assert!(self.doc_url.is_some()); - self.relayout(document, &(copy self.doc_url).get()); - } - } - return true; - } } } } +