From b17d9dc220dc69e600ee3b0ebdc90bc5423ec4bd Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Thu, 16 May 2013 18:15:37 -0700 Subject: [PATCH] Reduce the number of channels that talk to the script task to one --- src/components/servo/compositing/mod.rs | 18 +- .../servo/compositing/resize_rate_limiter.rs | 27 +-- src/components/servo/dom/document.rs | 10 +- src/components/servo/dom/window.rs | 49 +++-- src/components/servo/engine.rs | 18 +- src/components/servo/layout/layout_task.rs | 23 +-- src/components/servo/scripting/script_task.rs | 178 ++++++++---------- src/components/servo/servo.rc | 10 +- 8 files changed, 160 insertions(+), 173 deletions(-) diff --git a/src/components/servo/compositing/mod.rs b/src/components/servo/compositing/mod.rs index 053892bcfd2..5e9bf617c00 100644 --- a/src/components/servo/compositing/mod.rs +++ b/src/components/servo/compositing/mod.rs @@ -3,8 +3,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use compositing::resize_rate_limiter::ResizeRateLimiter; -use dom::event::Event; use platform::{Application, Window}; +use scripting::script_task::{LoadMsg, ScriptMsg}; use windowing::{ApplicationMethods, WindowMethods}; use azure::azure_hl::{BackendType, B8G8R8A8, DataSourceSurface, DrawTarget, SourceSurfaceMethods}; @@ -30,11 +30,11 @@ pub struct CompositorImpl { impl CompositorImpl { /// Creates a new compositor instance. - pub fn new(dom_event_chan: SharedChan, opts: Opts) -> CompositorImpl { - let dom_event_chan = Cell(dom_event_chan); + pub fn new(script_chan: SharedChan, opts: Opts) -> CompositorImpl { + let script_chan = Cell(script_chan); let chan: Chan = do on_osmain |port| { debug!("preparing to enter main loop"); - mainloop(port, dom_event_chan.take(), &opts); + mainloop(port, script_chan.take(), &opts); }; CompositorImpl { @@ -76,7 +76,7 @@ impl layers::layers::ImageData for AzureDrawTargetImageData { } } -fn mainloop(po: Port, dom_event_chan: SharedChan, opts: &Opts) { +fn mainloop(po: Port, script_chan: SharedChan, opts: &Opts) { let key_handlers: @mut ~[Chan<()>] = @mut ~[]; let app: Application = ApplicationMethods::new(); @@ -110,7 +110,7 @@ fn mainloop(po: Port, dom_event_chan: SharedChan, opts: &Opts) { identity()); let done = @mut false; - let resize_rate_limiter = @mut ResizeRateLimiter(dom_event_chan); + let resize_rate_limiter = @mut ResizeRateLimiter(script_chan.clone()); let check_for_messages: @fn() = || { // Periodically check if the script task responded to our last resize event resize_rate_limiter.check_resize_response(); @@ -197,6 +197,12 @@ fn mainloop(po: Port, dom_event_chan: SharedChan, opts: &Opts) { resize_rate_limiter.window_resized(width, height); } + // When the user enters a new URL, load it. + do window.set_load_url_callback |url_string| { + debug!("osmain: loading URL `%s`", url_string); + script_chan.send(LoadMsg(url::make_url(url_string.to_str(), None))) + } + // Enter the main event loop. while !*done { // Check for new messages coming from the rendering task. diff --git a/src/components/servo/compositing/resize_rate_limiter.rs b/src/components/servo/compositing/resize_rate_limiter.rs index 8c59b808062..efbf4e0ff53 100644 --- a/src/components/servo/compositing/resize_rate_limiter.rs +++ b/src/components/servo/compositing/resize_rate_limiter.rs @@ -2,27 +2,28 @@ * 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/. */ -/*! -A little class that rate limits the number of resize events sent to the script task -based on how fast script dispatches those events. It waits until each event is handled -before sending the next. If the window is resized multiple times before an event is handled -then some events will never be sent. -*/ +//! A little class that rate limits the number of resize events sent to the script task +/// based on how fast script dispatches those events. It waits until each event is handled +/// before sending the next. If the window is resized multiple times before an event is handled +/// then some events will never be sent. -use dom::event::{Event, ResizeEvent}; +use dom::event::ResizeEvent; +use scripting::script_task::{ScriptMsg, SendEventMsg}; + +use core::comm::{Port, SharedChan}; pub struct ResizeRateLimiter { /// The channel we send resize events on - /* priv */ dom_event_chan: comm::SharedChan, + priv script_chan: SharedChan, /// The port we are waiting on for a response to the last resize event - /* priv */ last_response_port: Option>, + priv last_response_port: Option>, /// The next window resize event we should fire - /* priv */ next_resize_event: Option<(uint, uint)> + priv next_resize_event: Option<(uint, uint)> } -pub fn ResizeRateLimiter(dom_event_chan: comm::SharedChan) -> ResizeRateLimiter { +pub fn ResizeRateLimiter(script_chan: SharedChan) -> ResizeRateLimiter { ResizeRateLimiter { - dom_event_chan: dom_event_chan, + script_chan: script_chan, last_response_port: None, next_resize_event: None } @@ -64,7 +65,7 @@ pub impl ResizeRateLimiter { priv fn send_event(&mut self, width: uint, height: uint) { let (port, chan) = comm::stream(); - self.dom_event_chan.send(ResizeEvent(width, height, chan)); + self.script_chan.send(SendEventMsg(ResizeEvent(width, height, chan))); self.last_response_port = Some(port); } } diff --git a/src/components/servo/dom/document.rs b/src/components/servo/dom/document.rs index 932cecbb8a6..393f5b0fdeb 100644 --- a/src/components/servo/dom/document.rs +++ b/src/components/servo/dom/document.rs @@ -8,7 +8,7 @@ use dom::event::ReflowEvent; use dom::htmlcollection::HTMLCollection; use dom::node::AbstractNode; use dom::window::Window; -use scripting::script_task::global_script_context; +use scripting::script_task::{SendEventMsg, global_script_context}; use js::jsapi::bindgen::{JS_AddObjectRoot, JS_RemoveObjectRoot}; use servo_util::tree::{TreeNodeRef, TreeUtils}; @@ -64,9 +64,9 @@ pub impl Document { } fn content_changed(&self) { - do self.window.map |window| { - let chan = &mut window.dom_event_chan; - chan.send(ReflowEvent) - }; + for self.window.each |window| { + window.script_chan.send(SendEventMsg(ReflowEvent)) + } } } + diff --git a/src/components/servo/dom/window.rs b/src/components/servo/dom/window.rs index 4651d0f0c9f..3c0e7b52477 100644 --- a/src/components/servo/dom/window.rs +++ b/src/components/servo/dom/window.rs @@ -4,8 +4,7 @@ use dom::bindings::utils::WrapperCache; use dom::bindings::window; -use dom::event::Event; -use scripting::script_task::{ControlMsg, ExitMsg, FireTimerMsg, ScriptContext}; +use scripting::script_task::{ExitMsg, FireTimerMsg, ScriptMsg, ScriptContext}; use scripting::script_task::{global_script_context}; use util::task::spawn_listener; @@ -24,7 +23,7 @@ pub enum TimerControlMsg { // only used for querying layout from arbitrary script. pub struct Window { timer_chan: Chan, - dom_event_chan: SharedChan, + script_chan: SharedChan, script_context: *mut ScriptContext, wrapper: WrapperCache } @@ -82,30 +81,30 @@ pub impl Window { &self.timer_chan, TimerMessage_Fire(~TimerData(argc, argv))); } -} -pub fn Window(script_chan: comm::SharedChan, - dom_event_chan: comm::SharedChan, - script_context: *mut ScriptContext) - -> @mut Window { - 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(FireTimerMsg(td)), - TimerMessage_TriggerExit => script_chan.send(ExitMsg), + pub fn new(script_chan: SharedChan, script_context: *mut ScriptContext) + -> @mut Window { + let script_chan_copy = script_chan.clone(); + let win = @mut Window { + wrapper: WrapperCache::new(), + script_chan: script_chan, + timer_chan: { + do spawn_listener |timer_port: Port| { + loop { + match timer_port.recv() { + TimerMessage_Close => break, + TimerMessage_Fire(td) => script_chan_copy.send(FireTimerMsg(td)), + TimerMessage_TriggerExit => script_chan_copy.send(ExitMsg), + } } } - } - }, - script_context: script_context, - }; + }, + script_context: script_context, + }; - let compartment = global_script_context().js_compartment; - window::create(compartment, win); - win + 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 341231736e6..05124f0bc81 100644 --- a/src/components/servo/engine.rs +++ b/src/components/servo/engine.rs @@ -3,10 +3,9 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use compositing::CompositorImpl; -use dom::event::Event; use layout::layout_task::LayoutTask; use layout::layout_task; -use scripting::script_task::{ExecuteMsg, LoadMsg, ScriptTask}; +use scripting::script_task::{ExecuteMsg, LoadMsg, ScriptMsg, ScriptTask}; use scripting::script_task; use util::task::spawn_listener; @@ -40,24 +39,23 @@ pub struct Engine { impl Engine { pub fn start(compositor: CompositorImpl, opts: &Opts, - dom_event_port: Port, - dom_event_chan: SharedChan, + script_port: Port, + script_chan: SharedChan, resource_task: ResourceTask, image_cache_task: ImageCacheTask) -> EngineTask { - let dom_event_port = Cell(dom_event_port); - let dom_event_chan = Cell(dom_event_chan); - + let (script_port, script_chan) = (Cell(script_port), Cell(script_chan)); let opts = Cell(copy *opts); + do spawn_listener:: |request| { let render_task = RenderTask(compositor.clone(), opts.with_ref(|o| copy *o)); let opts = opts.take(); let layout_task = LayoutTask(render_task.clone(), image_cache_task.clone(), opts); - let script_task = ScriptTask::new(layout_task.clone(), - dom_event_port.take(), - dom_event_chan.take(), + let script_task = ScriptTask::new(script_port.take(), + script_chan.take(), + layout_task.clone(), resource_task.clone(), image_cache_task.clone()); diff --git a/src/components/servo/layout/layout_task.rs b/src/components/servo/layout/layout_task.rs index b1ca5c79533..5e26e9f3d46 100644 --- a/src/components/servo/layout/layout_task.rs +++ b/src/components/servo/layout/layout_task.rs @@ -7,7 +7,7 @@ use css::matching::MatchMethods; use css::select::new_css_select_ctx; -use dom::event::{Event, ReflowEvent}; +use dom::event::ReflowEvent; use dom::node::{AbstractNode, LayoutData}; use layout::aux::LayoutAuxMethods; use layout::box_builder::LayoutTreeBuilder; @@ -15,6 +15,7 @@ use layout::context::LayoutContext; use layout::debug::{BoxedMutDebugMethods, DebugMethods}; use layout::display_list_builder::{DisplayListBuilder, FlowDisplayListBuilderMethods}; use layout::flow::FlowContext; +use scripting::script_task::{ScriptMsg, SendEventMsg}; use util::task::spawn_listener; use util::time::time; @@ -79,19 +80,19 @@ impl Damage { pub struct BuildData { node: AbstractNode, url: Url, - dom_event_chan: comm::SharedChan, + script_chan: SharedChan, window_size: Size2D, - script_join_chan: comm::Chan<()>, + script_join_chan: Chan<()>, damage: Damage, } pub fn LayoutTask(render_task: RenderTask, img_cache_task: ImageCacheTask, opts: Opts) -> LayoutTask { - SharedChan::new(spawn_listener::(|from_script| { + SharedChan::new(do spawn_listener:: |from_script| { let mut layout = Layout(render_task.clone(), img_cache_task.clone(), from_script, &opts); layout.start(); - })) + }) } struct Layout { @@ -170,8 +171,7 @@ impl Layout { let node = &data.node; // FIXME: Bad copy! let doc_url = copy data.url; - // FIXME: Bad clone! - let dom_event_chan = data.dom_event_chan.clone(); + let script_chan = data.script_chan.clone(); debug!("layout: received layout request for: %s", doc_url.to_str()); debug!("layout: damage is %?", data.damage); @@ -179,7 +179,7 @@ impl Layout { debug!("%?", node.dump()); // Reset the image cache. - self.local_image_cache.next_round(self.make_on_image_available_cb(dom_event_chan)); + self.local_image_cache.next_round(self.make_on_image_available_cb(script_chan)); let screen_size = Size2D(Au::from_px(data.window_size.width as int), Au::from_px(data.window_size.height as int)); @@ -315,15 +315,16 @@ impl Layout { // to the script task, and ultimately cause the image to be // re-requested. We probably don't need to go all the way back to // the script task for this. - fn make_on_image_available_cb(&self, dom_event_chan: comm::SharedChan) -> @fn() -> ~fn(ImageResponseMsg) { + fn make_on_image_available_cb(&self, script_chan: SharedChan) + -> @fn() -> ~fn(ImageResponseMsg) { // This has a crazy signature because the image cache needs to // make multiple copies of the callback, and the dom event // channel is not a copyable type, so this is actually a // little factory to produce callbacks let f: @fn() -> ~fn(ImageResponseMsg) = || { - let dom_event_chan = dom_event_chan.clone(); + let script_chan = script_chan.clone(); let f: ~fn(ImageResponseMsg) = |_| { - dom_event_chan.send(ReflowEvent) + script_chan.send(SendEventMsg(ReflowEvent)) }; f }; diff --git a/src/components/servo/scripting/script_task.rs b/src/components/servo/scripting/script_task.rs index eeb83358b0c..a89c2cdd654 100644 --- a/src/components/servo/scripting/script_task.rs +++ b/src/components/servo/scripting/script_task.rs @@ -20,7 +20,6 @@ use core::cell::Cell; use core::comm::{Port, SharedChan}; 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; @@ -40,12 +39,14 @@ use servo_util::tree::TreeNodeRef; use std::net::url::Url; use std::net::url; -/// Messages used to control the script task at a high level. -pub enum ControlMsg { +/// Messages used to control the script task. +pub enum ScriptMsg { /// Loads a new URL. LoadMsg(Url), /// Executes a standalone script. ExecuteMsg(Url), + /// Sends a DOM event. + SendEventMsg(Event), /// Fires a JavaScript timeout. FireTimerMsg(~TimerData), /// Exits the engine. @@ -54,42 +55,34 @@ pub enum ControlMsg { /// Encapsulates external communication with the script task. pub struct ScriptTask { - /// The channel used to send control messages to the script task. - chan: SharedChan, + /// The channel used to send messages to the script task. + chan: SharedChan, } impl ScriptTask { /// Creates a new script task. - pub fn new(layout_task: LayoutTask, - dom_event_port: Port, - dom_event_chan: SharedChan, + pub fn new(script_port: Port, + script_chan: SharedChan, + layout_task: LayoutTask, resource_task: ResourceTask, image_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 (script_chan_copy, script_port) = (script_chan.clone(), Cell(script_port)); // 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(), + script_port.take(), + script_chan_copy.clone(), resource_task.clone(), - image_cache_task.clone(), - dom_event_port.take(), - dom_event_chan.take()); + image_cache_task.clone()); script_context.start(); } ScriptTask { - chan: control_chan + chan: script_chan } } } @@ -116,16 +109,11 @@ pub struct ScriptContext { /// 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 + /// The port on which we receive messages (load URL, exit, etc.) + script_port: Port, + /// A channel for us to hand out when we want some other task to be able to send us script /// 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, + script_chan: SharedChan, /// The JavaScript runtime. js_runtime: js::rust::rt, @@ -173,12 +161,10 @@ impl Drop for ScriptContext { impl ScriptContext { /// Creates a new script context. pub fn new(layout_task: LayoutTask, - control_port: Port, - control_chan: SharedChan, + script_port: Port, + script_chan: SharedChan, resource_task: ResourceTask, - img_cache_task: ImageCacheTask, - event_port: Port, - event_chan: SharedChan) + img_cache_task: ImageCacheTask) -> @mut ScriptContext { let js_runtime = js::rust::rt(); let js_context = js_runtime.cx(); @@ -197,10 +183,8 @@ impl ScriptContext { resource_task: resource_task, layout_join_port: None, - control_port: control_port, - control_chan: control_chan, - event_port: event_port, - event_chan: event_chan, + script_port: script_port, + script_chan: script_chan, js_runtime: js_runtime, js_context: js_context, @@ -233,74 +217,72 @@ impl ScriptContext { /// Handles an incoming control message. fn handle_msg(&mut self) -> bool { - match select2i(&mut self.control_port, &mut self.event_port) { - Left(*) => { - let msg = self.control_port.recv(); - self.handle_control_msg(msg) - } - Right(*) => { - let ev = self.event_port.recv(); - self.handle_event(ev); + match self.script_port.recv() { + LoadMsg(url) => { + self.load(url); true } + ExecuteMsg(url) => { + self.handle_execute_msg(url); + true + } + SendEventMsg(event) => { + self.handle_event(event); + true + } + FireTimerMsg(timer_data) => { + self.handle_fire_timer_msg(timer_data); + true + } + ExitMsg => { + self.handle_exit_msg(); + false + } } } - /// 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 { - LoadMsg(url) => { - debug!("script: Received url `%s` to load", url::to_str(&url)); - self.load(url); - true - } + /// Handles a request to execute a script. + fn handle_execute_msg(&self, url: Url) { + debug!("script: Received url `%s` to execute", url::to_str(&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 - }; + match read_whole_file(&Path(url.path)) { + Err(msg) => println(fmt!("Error opening %s: %s", url::to_str(&url), msg)), - 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); - - self.relayout(); - 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); - } - } - - true - } - - ExitMsg => { - self.layout_task.send(layout_task::ExitMsg); - false + 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); } } } + /// Handles a timer that fired. + fn handle_fire_timer_msg(&mut self, timer_data: ~TimerData) { + let this_value = if timer_data.args.len() > 0 { + RUST_JSVAL_TO_OBJECT(timer_data.args[0]) + } else { + self.js_compartment.global_obj.ptr + }; + + // TODO: Support extra arguments. This requires passing a `*JSVal` array as `argv`. + let rval = JSVAL_NULL; + JS_CallFunctionValue(self.js_context.ptr, + this_value, + timer_data.funval, + 0, + null(), + &rval); + + self.relayout() + } + + /// Handles a request to exit the script task and shut down layout. + fn handle_exit_msg(&mut self) { + self.layout_task.send(layout_task::ExitMsg) + } + /// 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) { @@ -332,7 +314,7 @@ impl ScriptContext { 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 window = Window::new(self.script_chan.clone(), &mut *self); let document = Document(root_node, Some(window)); // Tie the root into the document. @@ -405,7 +387,7 @@ impl ScriptContext { let data = ~BuildData { node: root_frame.document.root, url: copy root_frame.url, - dom_event_chan: self.event_chan.clone(), + script_chan: self.script_chan.clone(), window_size: self.window_size, script_join_chan: join_chan, damage: replace(&mut self.damage, NoDamage), diff --git a/src/components/servo/servo.rc b/src/components/servo/servo.rc index a6964d9d153..3a124522265 100755 --- a/src/components/servo/servo.rc +++ b/src/components/servo/servo.rc @@ -133,11 +133,11 @@ fn main() { } fn run(opts: &Opts) { - let (dom_event_port, dom_event_chan) = comm::stream(); - let dom_event_chan = SharedChan::new(dom_event_chan); + let (script_port, script_chan) = comm::stream(); + let script_chan = SharedChan::new(script_chan); // The platform event handler thread - let compositor = CompositorImpl::new(dom_event_chan.clone(), copy *opts); + let compositor = CompositorImpl::new(script_chan.clone(), copy *opts); // Send each file to render then wait for keypress let (keypress_from_compositor, keypress_to_engine) = comm::stream(); @@ -148,8 +148,8 @@ fn run(opts: &Opts) { let image_cache_task = ImageCacheTask(resource_task.clone()); let engine_task = Engine::start(compositor.clone(), opts, - dom_event_port, - dom_event_chan, + script_port, + script_chan, resource_task, image_cache_task);