mirror of
https://github.com/servo/servo.git
synced 2025-08-05 05:30:08 +01:00
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:
parent
83e1bd81e8
commit
0a95672236
8 changed files with 194 additions and 135 deletions
|
@ -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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(),
|
||||||
|
|
|
@ -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};
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
103
src/components/main/layout_interface.rs
Normal file
103
src/components/main/layout_interface.rs
Normal 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>,
|
||||||
|
}
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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"]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue