Reduce coupling between layout and the DOM by separating out the layout interface.

Eventually, the layout interface will be moved along with the DOM into a
separate crate.
This commit is contained in:
Patrick Walton 2013-05-28 16:41:55 -07:00
parent 83e1bd81e8
commit 0a95672236
8 changed files with 194 additions and 135 deletions

View file

@ -8,7 +8,7 @@ use dom::bindings::utils::{domstring_to_jsval, WrapNewBindingObject};
use dom::bindings::utils::{str, CacheableWrapper, DOM_OBJECT_SLOT, DOMString}; use dom::bindings::utils::{str, CacheableWrapper, DOM_OBJECT_SLOT, DOMString};
use dom::element::*; use dom::element::*;
use dom::node::{AbstractNode, Element, ElementNodeTypeId, ScriptView}; use dom::node::{AbstractNode, Element, ElementNodeTypeId, ScriptView};
use layout::layout_task; use layout_interface::{ContentBoxQuery, ContentBoxResponse};
use scripting::script_task::task_from_context; use scripting::script_task::task_from_context;
use super::utils; use super::utils;
@ -216,10 +216,10 @@ extern fn HTMLImageElement_getWidth(cx: *JSContext, _argc: c_uint, vp: *mut JSVa
let width = match node.type_id() { let width = match node.type_id() {
ElementNodeTypeId(HTMLImageElementTypeId) => { ElementNodeTypeId(HTMLImageElementTypeId) => {
let script_context = task_from_context(cx); let script_context = task_from_context(cx);
match (*script_context).query_layout(layout_task::ContentBox(node)) { match (*script_context).query_layout(ContentBoxQuery(node)) {
Ok(rect) => { Ok(rect) => {
match rect { match rect {
layout_task::ContentRect(rect) => rect.size.width.to_px(), ContentBoxResponse(rect) => rect.size.width.to_px(),
_ => fail!(~"unexpected layout reply") _ => fail!(~"unexpected layout reply")
} }
} }

View file

@ -2,19 +2,17 @@
* 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/. */
// //! Element nodes.
// Element nodes.
//
use dom::node::{ElementNodeTypeId, Node, ScriptView}; use dom::bindings::utils::DOMString;
use dom::clientrect::ClientRect; use dom::clientrect::ClientRect;
use dom::clientrectlist::ClientRectList; use dom::clientrectlist::ClientRectList;
use dom::bindings::utils::DOMString; use dom::node::{ElementNodeTypeId, Node, ScriptView};
use layout_interface::{ContentBoxQuery, ContentBoxResponse, ContentBoxesQuery};
use layout_interface::{ContentBoxesResponse};
use layout::layout_task;
use core::str::eq_slice;
use core::cell::Cell; use core::cell::Cell;
use core::str::eq_slice;
use std::net::url::Url; use std::net::url::Url;
pub struct Element { pub struct Element {
@ -169,9 +167,9 @@ pub impl<'self> Element {
let script_context = unsafe { let script_context = unsafe {
&mut *win.script_context &mut *win.script_context
}; };
match script_context.query_layout(layout_task::ContentBoxes(node)) { match script_context.query_layout(ContentBoxesQuery(node)) {
Ok(rects) => match rects { Ok(rects) => match rects {
layout_task::ContentRects(rects) => ContentBoxesResponse(rects) =>
do rects.map |r| { do rects.map |r| {
ClientRect::new( ClientRect::new(
r.origin.y.to_f32(), r.origin.y.to_f32(),
@ -209,9 +207,9 @@ pub impl<'self> Element {
let node = self.parent.abstract.get(); let node = self.parent.abstract.get();
assert!(node.is_element()); assert!(node.is_element());
let script_context = unsafe { &mut *win.script_context }; let script_context = unsafe { &mut *win.script_context };
match script_context.query_layout(layout_task::ContentBox(node)) { match script_context.query_layout(ContentBoxQuery(node)) {
Ok(rect) => match rect { Ok(rect) => match rect {
layout_task::ContentRect(rect) => ContentBoxResponse(rect) =>
Some(ClientRect::new( Some(ClientRect::new(
rect.origin.y.to_f32(), rect.origin.y.to_f32(),
(rect.origin.y + rect.size.height).to_f32(), (rect.origin.y + rect.size.height).to_f32(),

View file

@ -4,8 +4,8 @@
use dom::bindings::utils::WrapperCache; use dom::bindings::utils::WrapperCache;
use dom::bindings::window; use dom::bindings::window;
use layout_interface::MatchSelectorsDamage;
use scripting::script_task::{ExitMsg, FireTimerMsg, ScriptMsg, ScriptContext}; use scripting::script_task::{ExitMsg, FireTimerMsg, ScriptMsg, ScriptContext};
use layout::layout_task::MatchSelectorsDamage;
use util::task::spawn_listener; use util::task::spawn_listener;
use core::comm::{Port, Chan, SharedChan}; use core::comm::{Port, Chan, SharedChan};

View file

@ -3,8 +3,9 @@
* 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 compositing::CompositorTask; use compositing::CompositorTask;
use layout::layout_task::LayoutTask;
use layout::layout_task; use layout::layout_task;
use layout_interface::LayoutTask;
use layout_interface;
use scripting::script_task::{ExecuteMsg, LoadMsg, ScriptMsg, ScriptTask}; use scripting::script_task::{ExecuteMsg, LoadMsg, ScriptMsg, ScriptTask};
use scripting::script_task; use scripting::script_task;
use util::task::spawn_listener; use util::task::spawn_listener;
@ -18,7 +19,6 @@ use servo_net::image_cache_task::{ImageCacheTask, ImageCacheTaskClient};
use servo_net::resource_task::ResourceTask; use servo_net::resource_task::ResourceTask;
use servo_net::resource_task; use servo_net::resource_task;
use servo_util::time::{ProfilerChan, ProfilerPort, ProfilerTask}; use servo_util::time::{ProfilerChan, ProfilerPort, ProfilerTask};
use servo_util::time;
use std::net::url::Url; use std::net::url::Url;
pub type EngineTask = Chan<Msg>; pub type EngineTask = Chan<Msg>;
@ -61,7 +61,7 @@ impl Engine {
let profiler_task = ProfilerTask::new(profiler_port.take(), profiler_chan.clone()); let profiler_task = ProfilerTask::new(profiler_port.take(), profiler_chan.clone());
let opts = opts.take(); let opts = opts.take();
let layout_task = LayoutTask(render_task.clone(), let layout_task = layout_task::create_layout_task(render_task.clone(),
image_cache_task.clone(), image_cache_task.clone(),
opts, opts,
profiler_task.chan.clone()); profiler_task.chan.clone());
@ -105,7 +105,7 @@ impl Engine {
ExitMsg(sender) => { ExitMsg(sender) => {
self.script_task.chan.send(script_task::ExitMsg); self.script_task.chan.send(script_task::ExitMsg);
self.layout_task.send(layout_task::ExitMsg); self.layout_task.chan.send(layout_interface::ExitMsg);
let (response_port, response_chan) = comm::stream(); let (response_port, response_chan) = comm::stream();

View file

@ -2,19 +2,23 @@
* 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/. */
/// The layout task. Performs layout on the DOM, builds display lists and sends them to be //! The layout task. Performs layout on the DOM, builds display lists and sends them to be
/// rendered. /// rendered.
use css::matching::MatchMethods; use css::matching::MatchMethods;
use css::select::new_css_select_ctx; use css::select::new_css_select_ctx;
use dom::event::ReflowEvent; use dom::event::ReflowEvent;
use dom::node::{AbstractNode, LayoutView, ScriptView}; use dom::node::{AbstractNode, LayoutView};
use layout::aux::{LayoutData, LayoutAuxMethods}; use layout::aux::{LayoutData, LayoutAuxMethods};
use layout::box_builder::LayoutTreeBuilder; use layout::box_builder::LayoutTreeBuilder;
use layout::context::LayoutContext; use layout::context::LayoutContext;
use layout::debug::{BoxedMutDebugMethods, DebugMethods}; use layout::debug::{BoxedMutDebugMethods, DebugMethods};
use layout::display_list_builder::{DisplayListBuilder, FlowDisplayListBuilderMethods}; use layout::display_list_builder::{DisplayListBuilder, FlowDisplayListBuilderMethods};
use layout::flow::FlowContext; use layout::flow::FlowContext;
use layout_interface::{AddStylesheetMsg, BuildData, BuildMsg, ContentBoxQuery, ContentBoxResponse};
use layout_interface::{ContentBoxesQuery, ContentBoxesResponse, ExitMsg, LayoutQuery};
use layout_interface::{LayoutResponse, LayoutTask, MatchSelectorsDamage, Msg, NoDamage, QueryMsg};
use layout_interface::{ReflowDamage};
use scripting::script_task::{ScriptMsg, SendEventMsg}; use scripting::script_task::{ScriptMsg, SendEventMsg};
use util::task::spawn_listener; use util::task::spawn_listener;
use servo_util::time; use servo_util::time;
@ -40,69 +44,24 @@ use newcss::types::OriginAuthor;
use servo_net::image_cache_task::{ImageCacheTask, ImageResponseMsg}; use servo_net::image_cache_task::{ImageCacheTask, ImageResponseMsg};
use servo_net::local_image_cache::LocalImageCache; use servo_net::local_image_cache::LocalImageCache;
use servo_util::tree::TreeUtils; use servo_util::tree::TreeUtils;
use std::net::url::Url;
pub type LayoutTask = SharedChan<Msg>; pub fn create_layout_task(render_task: RenderTask,
pub enum LayoutQuery {
ContentBox(AbstractNode<ScriptView>),
ContentBoxes(AbstractNode<ScriptView>),
}
pub type LayoutQueryResponse = Result<LayoutQueryResponse_, ()>;
pub enum LayoutQueryResponse_ {
ContentRect(Rect<Au>),
ContentRects(~[Rect<Au>])
}
pub enum Msg {
AddStylesheet(Stylesheet),
BuildMsg(~BuildData),
QueryMsg(LayoutQuery, Chan<LayoutQueryResponse>),
ExitMsg
}
// Dirty bits for layout.
pub enum Damage {
NoDamage, // Document is clean; do nothing.
ReflowDamage, // Reflow; don't perform CSS selector matching.
MatchSelectorsDamage, // Perform CSS selector matching and reflow.
}
impl Damage {
fn add(&mut self, new_damage: Damage) {
match (*self, new_damage) {
(NoDamage, _) => *self = new_damage,
(ReflowDamage, NoDamage) => *self = ReflowDamage,
(ReflowDamage, new_damage) => *self = new_damage,
(MatchSelectorsDamage, _) => *self = MatchSelectorsDamage
}
}
}
pub struct BuildData {
node: AbstractNode<ScriptView>,
url: Url,
script_chan: SharedChan<ScriptMsg>,
window_size: Size2D<uint>,
script_join_chan: Chan<()>,
damage: Damage,
}
pub fn LayoutTask(render_task: RenderTask,
img_cache_task: ImageCacheTask, img_cache_task: ImageCacheTask,
opts: Opts, opts: Opts,
prof_chan: ProfilerChan) profiler_chan: ProfilerChan)
-> LayoutTask { -> LayoutTask {
SharedChan::new(do spawn_listener::<Msg> |from_script| { let chan = do spawn_listener::<Msg> |from_script| {
let mut layout = Layout(render_task.clone(), let mut layout = Layout::new(render_task.clone(),
img_cache_task.clone(), img_cache_task.clone(),
from_script, from_script,
&opts, &opts,
prof_chan.clone()); profiler_chan.clone());
layout.start(); layout.start();
}) };
LayoutTask {
chan: SharedChan::new(chan),
}
} }
struct Layout { struct Layout {
@ -111,19 +70,22 @@ struct Layout {
local_image_cache: @mut LocalImageCache, local_image_cache: @mut LocalImageCache,
from_script: Port<Msg>, from_script: Port<Msg>,
font_ctx: @mut FontContext, font_ctx: @mut FontContext,
// This is used to root reader data
/// This is used to root reader data.
layout_refs: ~[@mut LayoutData], layout_refs: ~[@mut LayoutData],
css_select_ctx: @mut SelectCtx, css_select_ctx: @mut SelectCtx,
prof_chan: ProfilerChan, profiler_chan: ProfilerChan,
} }
fn Layout(render_task: RenderTask, impl Layout {
fn new(render_task: RenderTask,
image_cache_task: ImageCacheTask, image_cache_task: ImageCacheTask,
from_script: Port<Msg>, from_script: Port<Msg>,
opts: &Opts, opts: &Opts,
prof_chan: ProfilerChan) profiler_chan: ProfilerChan)
-> Layout { -> Layout {
let fctx = @mut FontContext::new(opts.render_backend, true, prof_chan.clone()); let fctx = @mut FontContext::new(opts.render_backend, true, profiler_chan.clone());
Layout { Layout {
render_task: render_task, render_task: render_task,
@ -133,35 +95,29 @@ fn Layout(render_task: RenderTask,
font_ctx: fctx, font_ctx: fctx,
layout_refs: ~[], layout_refs: ~[],
css_select_ctx: @mut new_css_select_ctx(), css_select_ctx: @mut new_css_select_ctx(),
prof_chan: prof_chan.clone() profiler_chan: profiler_chan,
}
} }
}
impl Layout {
fn start(&mut self) { fn start(&mut self) {
while self.handle_request() { while self.handle_request() {
// loop indefinitely // Loop indefinitely.
} }
} }
fn handle_request(&mut self) -> bool { fn handle_request(&mut self) -> bool {
match self.from_script.recv() { match self.from_script.recv() {
AddStylesheet(sheet) => { AddStylesheetMsg(sheet) => self.handle_add_stylesheet(sheet),
self.handle_add_stylesheet(sheet);
}
BuildMsg(data) => { BuildMsg(data) => {
let data = Cell(data); let data = Cell(data);
do profile(time::LayoutPerformCategory, self.prof_chan.clone()) { do profile(time::LayoutPerformCategory, self.profiler_chan.clone()) {
self.handle_build(data.take()); self.handle_build(data.take());
} }
} }
QueryMsg(query, chan) => { QueryMsg(query, chan) => {
let chan = Cell(chan); let chan = Cell(chan);
do profile(time::LayoutQueryCategory, self.prof_chan.clone()) { do profile(time::LayoutQueryCategory, self.profiler_chan.clone()) {
self.handle_query(query, chan.take()) self.handle_query(query, chan.take())
} }
} }
@ -212,7 +168,7 @@ impl Layout {
// Initialize layout data for each node. // Initialize layout data for each node.
// //
// FIXME: This is inefficient. We don't need an entire traversal to do this! // FIXME: This is inefficient. We don't need an entire traversal to do this!
do profile(time::LayoutAuxInitCategory, self.prof_chan.clone()) { do profile(time::LayoutAuxInitCategory, self.profiler_chan.clone()) {
node.initialize_style_for_subtree(&mut self.layout_refs); node.initialize_style_for_subtree(&mut self.layout_refs);
} }
@ -220,7 +176,7 @@ impl Layout {
match data.damage { match data.damage {
NoDamage | ReflowDamage => {} NoDamage | ReflowDamage => {}
MatchSelectorsDamage => { MatchSelectorsDamage => {
do profile(time::LayoutSelectorMatchCategory, self.prof_chan.clone()) { do profile(time::LayoutSelectorMatchCategory, self.profiler_chan.clone()) {
node.restyle_subtree(self.css_select_ctx); node.restyle_subtree(self.css_select_ctx);
} }
} }
@ -228,7 +184,7 @@ impl Layout {
// Construct the flow tree. // Construct the flow tree.
let layout_root: FlowContext = do profile(time::LayoutTreeBuilderCategory, let layout_root: FlowContext = do profile(time::LayoutTreeBuilderCategory,
self.prof_chan.clone()) { self.profiler_chan.clone()) {
let mut builder = LayoutTreeBuilder::new(); let mut builder = LayoutTreeBuilder::new();
let layout_root: FlowContext = match builder.construct_trees(&layout_ctx, *node) { let layout_root: FlowContext = match builder.construct_trees(&layout_ctx, *node) {
Ok(root) => root, Ok(root) => root,
@ -243,7 +199,7 @@ impl Layout {
// Perform the primary layout passes over the flow tree to compute the locations of all // Perform the primary layout passes over the flow tree to compute the locations of all
// the boxes. // the boxes.
do profile(time::LayoutMainCategory, self.prof_chan.clone()) { do profile(time::LayoutMainCategory, self.profiler_chan.clone()) {
for layout_root.traverse_postorder |flow| { for layout_root.traverse_postorder |flow| {
flow.bubble_widths(&mut layout_ctx); flow.bubble_widths(&mut layout_ctx);
}; };
@ -256,7 +212,7 @@ impl Layout {
} }
// Build the display list, and send it to the renderer. // Build the display list, and send it to the renderer.
do profile(time::LayoutDispListBuildCategory, self.prof_chan.clone()) { do profile(time::LayoutDispListBuildCategory, self.profiler_chan.clone()) {
let builder = DisplayListBuilder { let builder = DisplayListBuilder {
ctx: &layout_ctx, ctx: &layout_ctx,
}; };
@ -281,9 +237,9 @@ impl Layout {
/// Handles a query from the script task. This is the main routine that DOM functions like /// Handles a query from the script task. This is the main routine that DOM functions like
/// `getClientRects()` or `getBoundingClientRect()` ultimately invoke. /// `getClientRects()` or `getBoundingClientRect()` ultimately invoke.
fn handle_query(&self, query: LayoutQuery, reply_chan: Chan<LayoutQueryResponse>) { fn handle_query(&self, query: LayoutQuery, reply_chan: Chan<Result<LayoutResponse,()>>) {
match query { match query {
ContentBox(node) => { ContentBoxQuery(node) => {
// FIXME: Isolate this transmutation into a single "bridge" module. // FIXME: Isolate this transmutation into a single "bridge" module.
let node: AbstractNode<LayoutView> = unsafe { let node: AbstractNode<LayoutView> = unsafe {
transmute(node) transmute(node)
@ -308,14 +264,14 @@ impl Layout {
error!("no boxes for node"); error!("no boxes for node");
Err(()) Err(())
} }
Some(rect) => Ok(ContentRect(rect)) Some(rect) => Ok(ContentBoxResponse(rect))
} }
} }
}; };
reply_chan.send(response) reply_chan.send(response)
} }
ContentBoxes(node) => { ContentBoxesQuery(node) => {
// FIXME: Isolate this transmutation into a single "bridge" module. // FIXME: Isolate this transmutation into a single "bridge" module.
let node: AbstractNode<LayoutView> = unsafe { let node: AbstractNode<LayoutView> = unsafe {
transmute(node) transmute(node)
@ -329,7 +285,7 @@ impl Layout {
boxes.push(box.content_box()); boxes.push(box.content_box());
} }
Ok(ContentRects(boxes)) Ok(ContentBoxesResponse(boxes))
} }
}; };

View file

@ -0,0 +1,103 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
//! The high-level interface from script to layout. Using this abstract interface helps reduce
/// coupling between these two components, and enables the DOM to be placed in a separate crate
/// from layout.
use dom::node::{AbstractNode, ScriptView};
use scripting::script_task::ScriptMsg;
use core::comm::{Chan, SharedChan};
use geom::rect::Rect;
use geom::size::Size2D;
use gfx::geometry::Au;
use newcss::stylesheet::Stylesheet;
use std::net::url::Url;
/// Asynchronous messages that script can send to layout.
///
/// FIXME(pcwalton): I think this should probably be merged with `LayoutQuery` below.
pub enum Msg {
/// Adds the given stylesheet to the document.
AddStylesheetMsg(Stylesheet),
/// Requests a reflow.
///
/// FIXME(pcwalton): Call this `reflow` instead?
BuildMsg(~BuildData),
/// Performs a synchronous layout request.
///
/// FIXME(pcwalton): As noted below, this isn't very type safe.
QueryMsg(LayoutQuery, Chan<Result<LayoutResponse,()>>),
/// Requests that the layout task shut down and exit.
ExitMsg,
}
/// Synchronous messages that script can send to layout.
pub enum LayoutQuery {
/// Requests the dimensions of the content box, as in the `getBoundingClientRect()` call.
ContentBoxQuery(AbstractNode<ScriptView>),
/// Requests the dimensions of all the content boxes, as in the `getClientRects()` call.
ContentBoxesQuery(AbstractNode<ScriptView>),
}
/// The reply of a synchronous message from script to layout.
///
/// FIXME(pcwalton): This isn't very type safe. Maybe `LayoutQuery` objects should include
/// response channels?
pub enum LayoutResponse {
/// A response to the `ContentBoxQuery` message.
ContentBoxResponse(Rect<Au>),
/// A response to the `ContentBoxesQuery` message.
ContentBoxesResponse(~[Rect<Au>]),
}
/// Dirty bits for layout.
pub enum Damage {
/// The document is clean; nothing needs to be done.
NoDamage,
/// Reflow, but do not perform CSS selector matching.
ReflowDamage,
/// Perform CSS selector matching and reflow.
MatchSelectorsDamage,
}
impl Damage {
/// Sets this damage to the maximum of this damage and the given damage.
///
/// FIXME(pcwalton): This could be refactored to use `max` and the `Ord` trait, and this
/// function removed.
fn add(&mut self, new_damage: Damage) {
match (*self, new_damage) {
(NoDamage, _) => *self = new_damage,
(ReflowDamage, NoDamage) => *self = ReflowDamage,
(ReflowDamage, new_damage) => *self = new_damage,
(MatchSelectorsDamage, _) => *self = MatchSelectorsDamage
}
}
}
/// Information needed for a reflow.
pub struct BuildData {
node: AbstractNode<ScriptView>,
/// What reflow needs to be done.
damage: Damage,
/// The URL of the page.
url: Url,
/// The channel through which messages can be sent back to the script task.
script_chan: SharedChan<ScriptMsg>,
/// The current window size.
window_size: Size2D<uint>,
script_join_chan: Chan<()>,
}
/// Encapsulates a channel to the layout task.
#[deriving(Clone)]
pub struct LayoutTask {
chan: SharedChan<Msg>,
}

View file

@ -10,10 +10,10 @@ 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, LayoutQuery}; use layout_interface::{AddStylesheetMsg, BuildData, BuildMsg, Damage, LayoutQuery};
use layout::layout_task::{LayoutQueryResponse, LayoutTask, MatchSelectorsDamage, NoDamage}; use layout_interface::{LayoutResponse, LayoutTask, MatchSelectorsDamage, NoDamage};
use layout::layout_task::{QueryMsg, ReflowDamage}; use layout_interface::{QueryMsg, ReflowDamage};
use layout::layout_task; use layout_interface;
use core::cast::transmute; use core::cast::transmute;
use core::cell::Cell; use core::cell::Cell;
@ -292,7 +292,8 @@ impl ScriptContext {
for self.root_frame.each |frame| { for self.root_frame.each |frame| {
frame.document.teardown(); frame.document.teardown();
} }
self.layout_task.send(layout_task::ExitMsg)
self.layout_task.chan.send(layout_interface::ExitMsg)
} }
/// The entry point to document loading. Defines bindings, sets up the window and document /// The entry point to document loading. Defines bindings, sets up the window and document
@ -321,7 +322,7 @@ impl ScriptContext {
// in the script task. // in the script task.
loop { loop {
match html_parsing_result.style_port.recv() { match html_parsing_result.style_port.recv() {
Some(sheet) => self.layout_task.send(AddStylesheet(sheet)), Some(sheet) => self.layout_task.chan.send(AddStylesheetMsg(sheet)),
None => break, None => break,
} }
} }
@ -416,7 +417,7 @@ impl ScriptContext {
damage: replace(&mut self.damage, NoDamage), damage: replace(&mut self.damage, NoDamage),
}; };
self.layout_task.send(BuildMsg(data)) self.layout_task.chan.send(BuildMsg(data))
} }
} }
@ -424,11 +425,11 @@ impl ScriptContext {
} }
/// Sends the given query to layout. /// Sends the given query to layout.
pub fn query_layout(&mut self, query: LayoutQuery) -> LayoutQueryResponse { pub fn query_layout(&mut self, query: LayoutQuery) -> Result<LayoutResponse,()> {
self.join_layout(); self.join_layout();
let (response_port, response_chan) = comm::stream(); let (response_port, response_chan) = comm::stream();
self.layout_task.send(QueryMsg(query, response_chan)); self.layout_task.chan.send(QueryMsg(query, response_chan));
response_port.recv() response_port.recv()
} }

View file

@ -121,6 +121,7 @@ pub mod html {
pub mod hubbub_html_parser; pub mod hubbub_html_parser;
} }
pub mod layout_interface;
pub mod windowing; pub mod windowing;
#[path="platform/mod.rs"] #[path="platform/mod.rs"]