Reduce the number of channels that talk to the script task to one

This commit is contained in:
Patrick Walton 2013-05-16 18:15:37 -07:00
parent 6365becb6b
commit b17d9dc220
8 changed files with 160 additions and 173 deletions

View file

@ -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<Event>, opts: Opts) -> CompositorImpl {
let dom_event_chan = Cell(dom_event_chan);
pub fn new(script_chan: SharedChan<ScriptMsg>, opts: Opts) -> CompositorImpl {
let script_chan = Cell(script_chan);
let chan: Chan<Msg> = 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<Msg>, dom_event_chan: SharedChan<Event>, opts: &Opts) {
fn mainloop(po: Port<Msg>, script_chan: SharedChan<ScriptMsg>, opts: &Opts) {
let key_handlers: @mut ~[Chan<()>] = @mut ~[];
let app: Application = ApplicationMethods::new();
@ -110,7 +110,7 @@ fn mainloop(po: Port<Msg>, dom_event_chan: SharedChan<Event>, 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<Msg>, dom_event_chan: SharedChan<Event>, 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.

View file

@ -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<Event>,
priv script_chan: SharedChan<ScriptMsg>,
/// The port we are waiting on for a response to the last resize event
/* priv */ last_response_port: Option<comm::Port<()>>,
priv last_response_port: Option<Port<()>>,
/// 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<Event>) -> ResizeRateLimiter {
pub fn ResizeRateLimiter(script_chan: SharedChan<ScriptMsg>) -> 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);
}
}

View file

@ -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))
}
}
}

View file

@ -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<TimerControlMsg>,
dom_event_chan: SharedChan<Event>,
script_chan: SharedChan<ScriptMsg>,
script_context: *mut ScriptContext,
wrapper: WrapperCache
}
@ -82,22 +81,20 @@ pub impl Window {
&self.timer_chan,
TimerMessage_Fire(~TimerData(argc, argv)));
}
}
pub fn Window(script_chan: comm::SharedChan<ControlMsg>,
dom_event_chan: comm::SharedChan<Event>,
script_context: *mut ScriptContext)
pub fn new(script_chan: SharedChan<ScriptMsg>, script_context: *mut ScriptContext)
-> @mut Window {
let script_chan_copy = script_chan.clone();
let win = @mut Window {
wrapper: WrapperCache::new(),
dom_event_chan: dom_event_chan,
script_chan: script_chan,
timer_chan: {
do spawn_listener |timer_port: Port<TimerControlMsg>| {
loop {
match timer_port.recv() {
TimerMessage_Close => break,
TimerMessage_Fire(td) => script_chan.send(FireTimerMsg(td)),
TimerMessage_TriggerExit => script_chan.send(ExitMsg),
TimerMessage_Fire(td) => script_chan_copy.send(FireTimerMsg(td)),
TimerMessage_TriggerExit => script_chan_copy.send(ExitMsg),
}
}
}
@ -108,4 +105,6 @@ pub fn Window(script_chan: comm::SharedChan<ControlMsg>,
let compartment = global_script_context().js_compartment;
window::create(compartment, win);
win
}
}

View file

@ -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<Event>,
dom_event_chan: SharedChan<Event>,
script_port: Port<ScriptMsg>,
script_chan: SharedChan<ScriptMsg>,
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::<Msg> |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());

View file

@ -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<Event>,
script_chan: SharedChan<ScriptMsg>,
window_size: Size2D<uint>,
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::<Msg>(|from_script| {
SharedChan::new(do spawn_listener::<Msg> |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<Event>) -> @fn() -> ~fn(ImageResponseMsg) {
fn make_on_image_available_cb(&self, script_chan: SharedChan<ScriptMsg>)
-> @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
};

View file

@ -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<ControlMsg>,
/// The channel used to send messages to the script task.
chan: SharedChan<ScriptMsg>,
}
impl ScriptTask {
/// Creates a new script task.
pub fn new(layout_task: LayoutTask,
dom_event_port: Port<Event>,
dom_event_chan: SharedChan<Event>,
pub fn new(script_port: Port<ScriptMsg>,
script_chan: SharedChan<ScriptMsg>,
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<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
/// The port on which we receive messages (load URL, exit, etc.)
script_port: Port<ScriptMsg>,
/// A channel for us to hand out when we want some other task to be able to send us script
/// 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>,
script_chan: SharedChan<ScriptMsg>,
/// 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<ControlMsg>,
control_chan: SharedChan<ControlMsg>,
script_port: Port<ScriptMsg>,
script_chan: SharedChan<ScriptMsg>,
resource_task: ResourceTask,
img_cache_task: ImageCacheTask,
event_port: Port<Event>,
event_chan: SharedChan<Event>)
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,55 +217,37 @@ 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);
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 {
match self.script_port.recv() {
LoadMsg(url) => {
debug!("script: Received url `%s` to load", url::to_str(&url));
self.load(url);
true
}
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
};
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();
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
}
}
}
ExecuteMsg(url) => {
/// 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));
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,
@ -290,15 +256,31 @@ impl ScriptContext {
1);
}
}
true
}
ExitMsg => {
self.layout_task.send(layout_task::ExitMsg);
false
}
/// 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
@ -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),

View file

@ -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);