auto merge of #512 : metajack/servo/pcwalton-master, r=jdm

This is try number 2 for #501 rollup.
This commit is contained in:
bors-servo 2013-06-11 15:27:33 -07:00
commit 0ae2081e63
38 changed files with 952 additions and 366 deletions

6
.gitmodules vendored
View file

@ -88,3 +88,9 @@
[submodule "src/support/alert/rust-alert"]
path = src/support/alert/rust-alert
url = git://github.com/mozilla-servo/rust-alert.git
[submodule "src/support/nss/nss"]
path = src/support/nss/nss
url = git://github.com/mozilla-servo/nss.git
[submodule "src/support/nss/nspr"]
path = src/support/nss/nspr
url = git://github.com/mozilla-servo/nspr.git

15
configure vendored
View file

@ -411,6 +411,8 @@ CFG_SUBMODULES="\
support/libparserutils/libparserutils \
support/netsurfcss/libcss \
support/netsurfcss/rust-netsurfcss \
support/nss/nspr \
support/nss/nss \
support/opengles/rust-opengles \
support/sharegl/sharegl \
support/skia/skia \
@ -501,14 +503,17 @@ do
# needed because Azure's configure wants "--enable-skia"
CONFIGURE_ARGS=""
ENV_VARS=""
if [ $i = "rust-azure" ]; then
if [ $i = "support/azure/rust-azure" ]; then
CONFIGURE_ARGS="--enable-skia"
fi
if [ $i = mozjs ]; then
if [ ! -z $CFG_ENABLE_DEBUG ]; then
CONFIGURE_ARGS="--enable-debug"
fi
if [ $i = "support/nss/nspr" ]; then
CONFIGURE_ARGS="--enable-64bit"
fi
if [ $i = "support/spidermonkey/mozjs" ]; then
if [ ! -z $CFG_ENABLE_DEBUG ]; then
CONFIGURE_ARGS="--enable-debug"
fi
fi
if [ -f ${CONFIGURE_SCRIPT} ]
then

View file

@ -26,8 +26,10 @@ reftest: $(S)src/test/harness/reftest/reftest.rs servo
contenttest: $(S)src/test/harness/contenttest/contenttest.rs servo
$(RUSTC) $(RFLAGS_servo) -o $@ $< -L .
DEPS_CHECK_TARGETS_ALL = $(addprefix check-,$(DEPS_CHECK_ALL))
DEPS_CHECK_TARGETS_FAST = $(addprefix check-,$(filter-out $(SLOW_TESTS),$(DEPS_CHECK_ALL)))
DEPS_CHECK_TESTABLE = $(filter-out $(NO_TESTS),$(DEPS_CHECK_ALL))
DEPS_CHECK_TARGETS_ALL = $(addprefix check-,$(DEPS_CHECK_TESTABLE))
DEPS_CHECK_TARGETS_FAST = $(addprefix check-,$(filter-out $(SLOW_TESTS),$(DEPS_CHECK_TESTABLE)))
.PHONY: check $(DEPS_CHECK_TARGETS_ALL)

View file

@ -3,6 +3,12 @@ SLOW_TESTS += \
mozjs \
$(NULL)
# Tests for these submodules do not exist.
NO_TESTS += \
nspr \
nss \
$(NULL)
# These submodules will not be cleaned by the `make clean-fast` target.
SLOW_BUILDS += \
libcss \
@ -21,6 +27,8 @@ NATIVE_BUILDS += \
libwapcaplet \
mozjs \
skia \
nss \
nspr \
$(NULL)
# NOTE: the make magic can only compute transitive build dependencies,
@ -40,6 +48,7 @@ DEPS_rust-glut += \
DEPS_rust-layers += \
rust-azure \
rust-cocoa \
rust-geom \
rust-glut \
rust-opengles \
@ -128,6 +137,10 @@ DEPS_rust-layers += \
rust-core-text \
$(NULL)
DEPS_rust-glut += \
rust-cocoa \
$(NULL)
endif
ifeq ($(CFG_OSTYPE),unknown-linux-gnu)

View file

@ -4,6 +4,7 @@
use azure::azure_hl::{DrawTarget};
use geom::rect::Rect;
use geom::size::Size2D;
pub struct LayerBuffer {
draw_target: DrawTarget,
@ -24,6 +25,6 @@ pub struct LayerBufferSet {
/// The interface used to by the renderer to acquire draw targets for each rendered frame and
/// submit them to be drawn to the display.
pub trait Compositor {
fn paint(&self, layer_buffer_set: LayerBufferSet);
fn paint(&self, layer_buffer_set: LayerBufferSet, new_size: Size2D<uint>);
}

View file

@ -19,6 +19,7 @@ use geometry::Au;
use render_context::RenderContext;
use text::SendableTextRun;
use core::cast::transmute_region;
use geom::{Point2D, Rect, Size2D};
use servo_net::image::base::Image;
use servo_util::range::Range;
@ -26,20 +27,20 @@ use std::arc::ARC;
use std::arc;
/// A list of rendering operations to be performed.
pub struct DisplayList {
priv list: ~[DisplayItem]
pub struct DisplayList<E> {
list: ~[DisplayItem<E>]
}
impl DisplayList {
impl<E> DisplayList<E> {
/// Creates a new display list.
pub fn new() -> DisplayList {
pub fn new() -> DisplayList<E> {
DisplayList {
list: ~[]
}
}
/// Appends the given item to the display list.
pub fn append_item(&mut self, item: DisplayItem) {
pub fn append_item(&mut self, item: DisplayItem<E>) {
// FIXME(Issue #150): crashes
//debug!("Adding display item %u: %?", self.len(), item);
self.list.push(item)
@ -58,51 +59,54 @@ impl DisplayList {
}
/// One drawing command in the list.
pub enum DisplayItem {
SolidColorDisplayItemClass(~SolidColorDisplayItem),
TextDisplayItemClass(~TextDisplayItem),
ImageDisplayItemClass(~ImageDisplayItem),
BorderDisplayItemClass(~BorderDisplayItem),
pub enum DisplayItem<E> {
SolidColorDisplayItemClass(~SolidColorDisplayItem<E>),
TextDisplayItemClass(~TextDisplayItem<E>),
ImageDisplayItemClass(~ImageDisplayItem<E>),
BorderDisplayItemClass(~BorderDisplayItem<E>),
}
/// Information common to all display items.
pub struct BaseDisplayItem {
pub struct BaseDisplayItem<E> {
/// The boundaries of the display item.
///
/// TODO: Which coordinate system should this use?
bounds: Rect<Au>,
/// Extra data: either the originating flow (for hit testing) or nothing (for rendering).
extra: E,
}
/// Renders a solid color.
pub struct SolidColorDisplayItem {
base: BaseDisplayItem,
pub struct SolidColorDisplayItem<E> {
base: BaseDisplayItem<E>,
color: Color,
}
/// Renders text.
pub struct TextDisplayItem {
base: BaseDisplayItem,
pub struct TextDisplayItem<E> {
base: BaseDisplayItem<E>,
text_run: ~SendableTextRun,
range: Range,
color: Color,
}
/// Renders an image.
pub struct ImageDisplayItem {
base: BaseDisplayItem,
pub struct ImageDisplayItem<E> {
base: BaseDisplayItem<E>,
image: ARC<~Image>,
}
/// Renders a border.
pub struct BorderDisplayItem {
base: BaseDisplayItem,
pub struct BorderDisplayItem<E> {
base: BaseDisplayItem<E>,
/// The width of the border.
width: Au,
/// The color of the border.
color: Color,
}
impl DisplayItem {
impl<E> DisplayItem<E> {
/// Renders this display item into the given render context.
fn draw_into_context(&self, render_context: &RenderContext) {
match *self {
@ -148,5 +152,21 @@ impl DisplayItem {
}
}
}
fn base<'a>(&'a self) -> &'a BaseDisplayItem<E> {
// FIXME(tkuehn): Workaround for Rust region bug.
unsafe {
match *self {
SolidColorDisplayItemClass(ref solid_color) => transmute_region(&solid_color.base),
TextDisplayItemClass(ref text) => transmute_region(&text.base),
ImageDisplayItemClass(ref image_item) => transmute_region(&image_item.base),
BorderDisplayItemClass(ref border) => transmute_region(&border.base)
}
}
}
fn bounds(&self) -> Rect<Au> {
self.base().bounds
}
}

View file

@ -6,7 +6,7 @@ use geom::point::Point2D;
use geom::rect::Rect;
use geom::size::Size2D;
use core::num::NumCast;
use core::num::{NumCast, One, Zero};
pub struct Au(i32);
@ -46,6 +46,17 @@ impl cmp::Eq for Au {
fn ne(&self, other: &Au) -> bool { **self != **other }
}
impl One for Au {
fn one() -> Au { Au(1) }
}
impl Zero for Au {
fn zero() -> Au { Au(0) }
fn is_zero(&self) -> bool { **self == 0 }
}
impl Num for Au {}
pub fn min(x: Au, y: Au) -> Au { if x < y { x } else { y } }
pub fn max(x: Au, y: Au) -> Au { if x > y { x } else { y } }

View file

@ -14,8 +14,12 @@ use geom::point::Point2D;
use geom::rect::Rect;
use geom::size::Size2D;
/// The type representing the lack of extra display list data. This is used when sending display
/// list data off to be rendered.
pub type Nothing = ();
pub struct RenderLayer {
display_list: DisplayList,
display_list: DisplayList<Nothing>,
size: Size2D<uint>
}
@ -50,8 +54,6 @@ pub fn render_layers(layer_ref: *RenderLayer,
let width = right - x;
let height = bottom - y;
let tile_rect = Rect(Point2D(x, y), Size2D(width, height));
// Round the width up the nearest 32 pixels for DMA on the Mac.
let aligned_width = if width % 32 == 0 {
width
@ -63,6 +65,8 @@ pub fn render_layers(layer_ref: *RenderLayer,
debug!("tile aligned_width %u", aligned_width);
let tile_rect = Rect(Point2D(x, y), Size2D(aligned_width, height));
let buffer;
// FIXME: Try harder to search for a matching tile.
// FIXME: Don't use shift; it's bad for perf. Maybe reverse and pop.
@ -75,7 +79,7 @@ pub fn render_layers(layer_ref: *RenderLayer,
let size = Size2D(aligned_width as i32, height as i32);
// FIXME: This may not be always true.
let stride = size.width * 4;
let stride = (aligned_width as i32) * 4;
let mut data: ~[u8] = ~[0];
let offset;

View file

@ -162,7 +162,7 @@ impl<C: Compositor + Owned> Renderer<C> {
};
debug!("renderer: returning surface");
self.compositor.paint(layer_buffer_set);
self.compositor.paint(layer_buffer_set, render_layer.size);
}
}
}

View file

@ -4,12 +4,14 @@
use compositing::resize_rate_limiter::ResizeRateLimiter;
use platform::{Application, Window};
use script::script_task::{LoadMsg, ScriptMsg};
use script::script_task::{LoadMsg, ScriptMsg, SendEventMsg};
use windowing::{ApplicationMethods, WindowMethods};
use script::dom::event::ClickEvent;
use azure::azure_hl::{DataSourceSurface, DrawTarget, SourceSurfaceMethods};
use core::cell::Cell;
use core::comm::{Chan, SharedChan, Port};
use core::num::Orderable;
use core::util;
use geom::matrix::identity;
use geom::point::Point2D;
@ -59,8 +61,10 @@ impl CompositorTask {
/// Messages to the compositor.
pub enum Msg {
Paint(LayerBufferSet),
Exit
/// Requests that the compositor paint the given layer buffer set for the given page size.
Paint(LayerBufferSet, Size2D<uint>),
/// Requests that the compositor shut down.
Exit,
}
/// Azure surface wrapping to work with the layers infrastructure.
@ -119,6 +123,8 @@ fn run_main_loop(port: Port<Msg>,
// applied to the layers themselves on a per-layer basis. However, this won't work until scroll
// positions are sent to content.
let world_offset = @mut Point2D(0f32, 0f32);
let page_size = @mut Size2D(0f32, 0f32);
let window_size = @mut Size2D(800, 600);
let check_for_messages: @fn() = || {
// Periodically check if the script task responded to our last resize event
@ -129,15 +135,20 @@ fn run_main_loop(port: Port<Msg>,
match port.recv() {
Exit => *done = true,
Paint(new_layer_buffer_set) => {
Paint(new_layer_buffer_set, new_size) => {
debug!("osmain: received new frame");
*page_size = Size2D(new_size.width as f32, new_size.height as f32);
let mut new_layer_buffer_set = new_layer_buffer_set;
// Iterate over the children of the container layer.
let mut current_layer_child = root_layer.first_child;
// Replace the image layer data with the buffer data.
// Replace the image layer data with the buffer data. Also compute the page
// size here.
let buffers = util::replace(&mut new_layer_buffer_set.buffers, ~[]);
for buffers.each |buffer| {
let width = buffer.rect.size.width as uint;
let height = buffer.rect.size.height as uint;
@ -172,9 +183,10 @@ fn run_main_loop(port: Port<Msg>,
Some(_) => fail!(~"found unexpected layer kind"),
};
let origin = buffer.rect.origin;
let origin = Point2D(origin.x as f32, origin.y as f32);
// Set the layer's transform.
let origin = Point2D(buffer.rect.origin.x as f32,
buffer.rect.origin.y as f32);
let transform = original_layer_transform.translate(origin.x,
origin.y,
0.0);
@ -184,6 +196,8 @@ fn run_main_loop(port: Port<Msg>,
// TODO: Recycle the old buffers; send them back to the renderer to reuse if
// it wishes.
window.set_needs_display()
}
}
}
@ -205,24 +219,45 @@ fn run_main_loop(port: Port<Msg>,
// Hook the windowing system's resize callback up to the resize rate limiter.
do window.set_resize_callback |width, height| {
debug!("osmain: window resized to %ux%u", width, height);
resize_rate_limiter.window_resized(width, height);
*window_size = Size2D(width, height);
resize_rate_limiter.window_resized(width, height)
}
let script_chan_clone = script_chan.clone();
// 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)))
script_chan_clone.send(LoadMsg(url::make_url(url_string.to_str(), None)))
}
let script_chan_clone = script_chan.clone();
// When the user clicks, perform hit testing
do window.set_click_callback |layer_click_point| {
let world_click_point = layer_click_point + *world_offset;
debug!("osmain: clicked at %?", world_click_point);
script_chan_clone.send(SendEventMsg(ClickEvent(world_click_point)));
}
// When the user scrolls, move the layer around.
do window.set_scroll_callback |delta| {
// FIXME (Rust #2528): Can't use `+=`.
// FIXME (Rust #2528): Can't use `-=`.
let world_offset_copy = *world_offset;
*world_offset = world_offset_copy + delta;
*world_offset = world_offset_copy - delta;
// Clamp the world offset to the screen size.
let max_x = (page_size.width - window_size.width as f32).max(&0.0);
world_offset.x = world_offset.x.clamp(&0.0, &max_x);
let max_y = (page_size.height - window_size.height as f32).max(&0.0);
world_offset.y = world_offset.y.clamp(&0.0, &max_y);
debug!("compositor: scrolled to %?", *world_offset);
root_layer.common.set_transform(identity().translate(world_offset.x, world_offset.y, 0.0));
root_layer.common.set_transform(identity().translate(-world_offset.x,
-world_offset.y,
0.0));
window.set_needs_display()
}
@ -241,8 +276,8 @@ fn run_main_loop(port: Port<Msg>,
/// Implementation of the abstract `Compositor` interface.
impl Compositor for CompositorTask {
fn paint(&self, layer_buffer_set: LayerBufferSet) {
self.chan.send(Paint(layer_buffer_set))
fn paint(&self, layer_buffer_set: LayerBufferSet, new_size: Size2D<uint>) {
self.chan.send(Paint(layer_buffer_set, new_size))
}
}

View file

@ -121,6 +121,9 @@ h1 { page-break-before: always }
h4, h5, h6 { page-break-after: avoid }
ul, ol, dl { page-break-before: avoid }
}
/* Servo additions */
:link { color: blue }
"
}

View file

@ -76,6 +76,16 @@ impl SelectHandler<AbstractNode<LayoutView>> for NodeSelectHandler {
self.parent_node(node).is_none()
}
fn node_is_link(&self, node: &AbstractNode<LayoutView>) -> bool {
if node.is_element() {
do node.with_imm_element |element| {
"a" == element.tag_name
}
} else {
false
}
}
fn with_node_id<R>(&self, node: &AbstractNode<LayoutView>, f: &fn(Option<&str>) -> R) -> R {
if !node.is_element() {
fail!(~"attempting to style non-element node");

View file

@ -6,16 +6,18 @@
use layout::box::{RenderBox};
use layout::context::LayoutContext;
use layout::display_list_builder::{DisplayListBuilder, FlowDisplayListBuilderMethods};
use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData};
use layout::display_list_builder::{FlowDisplayListBuilderMethods};
use layout::flow::{BlockFlow, FlowContext, FlowData, InlineBlockFlow};
use layout::inline::InlineLayout;
use layout::model::{MaybeAuto, Specified, Auto};
use au = gfx::geometry;
use core::cell::Cell;
use geom::point::Point2D;
use geom::rect::Rect;
use gfx::display_list::DisplayList;
use gfx::geometry::Au;
use gfx::geometry;
use servo_util::tree::{TreeNodeRef, TreeUtils};
pub struct BlockFlowData {
@ -95,14 +97,19 @@ impl BlockFlowData {
assert!(child_ctx.starts_block_flow() || child_ctx.starts_inline_flow());
do child_ctx.with_base |child_node| {
min_width = au::max(min_width, child_node.min_width);
pref_width = au::max(pref_width, child_node.pref_width);
min_width = geometry::max(min_width, child_node.min_width);
pref_width = geometry::max(pref_width, child_node.pref_width);
}
}
/* if not an anonymous block context, add in block box's widths.
these widths will not include child elements, just padding etc. */
self.box.map(|&box| {
//Can compute border width here since it doesn't depend on anything
let style = box.style();
do box.with_model |model| {
model.compute_borders(style)
}
min_width = min_width.add(&box.get_min_width(ctx));
pref_width = pref_width.add(&box.get_pref_width(ctx));
});
@ -111,6 +118,57 @@ impl BlockFlowData {
self.common.pref_width = pref_width;
}
/// Computes left and right margins and width based on CSS 2.1 secion 10.3.3.
/// Requires borders and padding to already be computed
priv fn compute_horiz( &self,
width: MaybeAuto,
left_margin: MaybeAuto,
right_margin: MaybeAuto,
available_width: Au) -> (Au, Au, Au) {
//If width is not 'auto', and width + margins > available_width, all 'auto' margins are treated as '0'
let (left_margin, right_margin) = match width{
Auto => (left_margin, right_margin),
Specified(width) => {
let left = left_margin.spec_or_default(Au(0));
let right = right_margin.spec_or_default(Au(0));
if((left + right + width) > available_width) {
(Specified(left), Specified(right))
} else {
(left_margin, right_margin)
}
}
};
//Invariant: left_margin_Au + width_Au + right_margin_Au == available_width
let (left_margin_Au, width_Au, right_margin_Au) = match (left_margin, width, right_margin) {
//If all have a computed value other than 'auto', the system is over-constrained and we need to discard a margin.
//if direction is ltr, ignore the specified right margin and solve for it. If it is rtl, ignore the specified
//left margin. FIXME(eatkinson): this assumes the direction is ltr
(Specified(margin_l), Specified(width), Specified(_margin_r)) => (margin_l, width, available_width - (margin_l + width )),
//If exactly one value is 'auto', solve for it
(Auto, Specified(width), Specified(margin_r)) => (available_width - (width + margin_r), width, margin_r),
(Specified(margin_l), Auto, Specified(margin_r)) => (margin_l, available_width - (margin_l + margin_r), margin_r),
(Specified(margin_l), Specified(width), Auto) => (margin_l, width, available_width - (margin_l + width)),
//If width is set to 'auto', any other 'auto' value becomes '0', and width is solved for
(Auto, Auto, Specified(margin_r)) => (Au(0), available_width - margin_r, margin_r),
(Specified(margin_l), Auto, Auto) => (margin_l, available_width - margin_l, Au(0)),
(Auto, Auto, Auto) => (Au(0), available_width, Au(0)),
//If left and right margins are auto, they become equal
(Auto, Specified(width), Auto) => {
let margin = (available_width - width).scale_by(0.5);
(margin, width, margin)
}
};
//return values in same order as params
(width_Au, left_margin_Au, right_margin_Au)
}
/// Recursively (top-down) determines the actual width of child contexts and boxes. When called
/// on this context, the context has had its width set by the parent context.
///
@ -124,24 +182,60 @@ impl BlockFlowData {
self.common.position.size.width = ctx.screen_size.size.width;
}
//position was set to the containing block by the flow's parent
let mut remaining_width = self.common.position.size.width;
let left_used = Au(0);
let mut x_offset = Au(0);
// Let the box consume some width. It will return the amount remaining for its children.
self.box.map(|&box| {
do box.with_mut_base |base| {
base.position.size.width = remaining_width;
for self.box.each |&box| {
let style = box.style();
do box.with_model |model| {
// Can compute padding here since we know containing block width.
model.compute_padding(style, remaining_width);
let (left_used, right_used) = box.get_used_width();
remaining_width -= left_used.add(&right_used);
// Margins are 0 right now so model.noncontent_width() is just borders + padding.
let available_width = remaining_width - model.noncontent_width();
// Top and bottom margins for blocks are 0 if auto.
let margin_top = MaybeAuto::from_margin(style.margin_top());
let margin_top = margin_top.spec_or_default(Au(0));
let margin_bottom = MaybeAuto::from_margin(style.margin_bottom());
let margin_bottom = margin_bottom.spec_or_default(Au(0));
let (width, margin_left, margin_right) =
(MaybeAuto::from_width(style.width()),
MaybeAuto::from_margin(style.margin_left()),
MaybeAuto::from_margin(style.margin_right()));
// FIXME(pcwalton): We discard the width here. Is that correct?
let (_, margin_left, margin_right) = self.compute_horiz(width,
margin_left,
margin_right,
available_width);
model.margin.top = margin_top;
model.margin.right = margin_right;
model.margin.bottom = margin_bottom;
model.margin.left = margin_left;
x_offset = model.offset();
remaining_width = remaining_width - model.noncontent_width();
}
});
do box.with_mut_base |base| {
//The associated box is the border box of this flow
base.position.origin.x = base.model.margin.left;
let pb = base.model.padding.left + base.model.padding.right +
base.model.border.left + base.model.border.right;
base.position.size.width = remaining_width + pb;
}
}
for BlockFlow(self).each_child |kid| {
assert!(kid.starts_block_flow() || kid.starts_inline_flow());
do kid.with_mut_base |child_node| {
child_node.position.origin.x = left_used;
child_node.position.origin.x = x_offset;
child_node.position.size.width = remaining_width;
}
}
@ -150,6 +244,12 @@ impl BlockFlowData {
pub fn assign_height_block(@mut self, ctx: &LayoutContext) {
let mut cur_y = Au(0);
for self.box.each |&box| {
do box.with_model |model| {
cur_y += model.margin.top + model.border.top + model.padding.top;
}
}
for BlockFlow(self).each_child |kid| {
do kid.with_mut_base |child_node| {
child_node.position.origin.y = cur_y;
@ -157,28 +257,32 @@ impl BlockFlowData {
}
}
let height = if self.is_root { Au::max(ctx.screen_size.size.height, cur_y) }
else { cur_y };
let height = if self.is_root {
Au::max(ctx.screen_size.size.height, cur_y)
} else {
cur_y
};
//TODO(eatkinson): compute heights using the 'height' property.
self.common.position.size.height = height;
let _used_top = Au(0);
let _used_bot = Au(0);
self.box.map(|&box| {
do box.with_mut_base |base| {
base.position.origin.y = Au(0);
base.position.size.height = height;
let (_used_top, _used_bot) = box.get_used_height();
//The associated box is the border box of this flow
base.position.origin.y = base.model.margin.top;
let pb = base.model.padding.top + base.model.padding.bottom +
base.model.border.top + base.model.border.bottom;
base.position.size.height = height + pb;
}
});
}
pub fn build_display_list_block(@mut self,
builder: &DisplayListBuilder,
dirty: &Rect<Au>,
offset: &Point2D<Au>,
list: &Cell<DisplayList>) {
pub fn build_display_list_block<E:ExtraDisplayListData>(@mut self,
builder: &DisplayListBuilder,
dirty: &Rect<Au>,
offset: &Point2D<Au>,
list: &Cell<DisplayList<E>>) {
// add box that starts block context
self.box.map(|&box| {
box.build_display_list(builder, dirty, offset, list)
@ -194,3 +298,4 @@ impl BlockFlowData {
}
}
}

View file

@ -6,13 +6,15 @@
use css::node_style::StyledNode;
use layout::context::LayoutContext;
use layout::display_list_builder::{DisplayListBuilder, ToGfxColor};
use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData, ToGfxColor};
use layout::flow::FlowContext;
use layout::model::BoxModel;
use layout::text;
use core::cell::Cell;
use core::cmp::ApproxEq;
use core::managed;
use core::num::Zero;
use geom::{Point2D, Rect, Size2D};
use gfx::display_list::{BaseDisplayItem, BorderDisplayItem, BorderDisplayItemClass};
use gfx::display_list::{DisplayList, ImageDisplayItem, ImageDisplayItemClass};
@ -24,7 +26,6 @@ use gfx::text::text_run::TextRun;
use newcss::color::rgb;
use newcss::complete::CompleteStyle;
use newcss::units::{Cursive, Em, Fantasy, Monospace, Pt, Px, SansSerif, Serif};
use newcss::values::{CSSBorderWidthLength, CSSBorderWidthMedium};
use newcss::values::{CSSFontFamilyFamilyName, CSSFontFamilyGenericFamily};
use newcss::values::{CSSFontSizeLength, CSSFontStyleItalic, CSSFontStyleNormal};
use newcss::values::{CSSFontStyleOblique, CSSTextAlign, CSSTextDecoration};
@ -154,6 +155,9 @@ pub struct RenderBoxBase {
/// The position of this box relative to its owning flow.
position: Rect<Au>,
/// The core parameters (border, padding, margin) used by the box model.
model: BoxModel,
/// A debug ID.
///
/// TODO(#87) Make this only present in debug builds.
@ -168,6 +172,7 @@ impl RenderBoxBase {
node: node,
ctx: flow_context,
position: Au::zero_rect(),
model: Zero::zero(),
id: id,
}
}
@ -176,7 +181,7 @@ impl RenderBoxBase {
pub impl RenderBox {
/// Borrows this render box immutably in order to work with its common data.
#[inline(always)]
fn with_imm_base<R>(&self, callback: &fn(&RenderBoxBase) -> R) -> R {
fn with_base<R>(&self, callback: &fn(&RenderBoxBase) -> R) -> R {
match *self {
GenericRenderBoxClass(generic_box) => callback(generic_box),
ImageRenderBoxClass(image_box) => {
@ -210,7 +215,7 @@ pub impl RenderBox {
/// A convenience function to return the position of this box.
fn position(&self) -> Rect<Au> {
do self.with_imm_base |base| {
do self.with_base |base| {
base.position
}
}
@ -367,6 +372,8 @@ pub impl RenderBox {
/// Returns the *minimum width* of this render box as defined by the CSS specification.
fn get_min_width(&self, _: &LayoutContext) -> Au {
// FIXME(pcwalton): I think we only need to calculate this if the damage says that CSS
// needs to be restyled.
match *self {
// TODO: This should account for the minimum width of the box element in isolation.
// That includes borders, margins, and padding, but not child widths. The block
@ -441,37 +448,36 @@ pub impl RenderBox {
(Au(0), Au(0))
}
fn compute_padding(&self, cb_width: Au) {
do self.with_mut_base |base| {
base.model.compute_padding(base.node.style(), cb_width);
}
}
fn get_noncontent_width(&self) -> Au {
do self.with_base |base| {
base.model.border.left + base.model.padding.left +
base.model.border.right + base.model.padding.right
}
}
fn with_model<R>(&self, callback: &fn(&mut BoxModel) -> R) -> R {
do self.with_mut_base |base| {
callback(&mut base.model)
}
}
/// The box formed by the content edge as defined in CSS 2.1 § 8.1. Coordinates are relative to
/// the owning flow.
fn content_box(&self) -> Rect<Au> {
let origin = self.position().origin;
match *self {
ImageRenderBoxClass(image_box) => {
Rect {
origin: origin,
size: image_box.base.position.size,
}
},
GenericRenderBoxClass(*) => {
self.position()
// FIXME: The following hits an ICE for whatever reason.
/*
let origin = self.d().position.origin;
let size = self.d().position.size;
let (offset_left, offset_right) = self.get_used_width();
let (offset_top, offset_bottom) = self.get_used_height();
Rect {
origin: Point2D(origin.x + offset_left, origin.y + offset_top),
size: Size2D(size.width - (offset_left + offset_right),
size.height - (offset_top + offset_bottom))
}
*/
},
TextRenderBoxClass(*) => self.position(),
UnscannedTextRenderBoxClass(*) => fail!(~"Shouldn't see unscanned boxes here.")
do self.with_base |base| {
let origin = Point2D(base.position.origin.x +
base.model.border.left +
base.model.padding.left,
base.position.origin.y);
let size = Size2D(base.position.size.width - self.get_noncontent_width(),
base.position.size.height);
Rect(origin, size)
}
}
@ -489,22 +495,15 @@ pub impl RenderBox {
self.content_box()
}
/// A convenience function to determine whether this render box represents a DOM element.
fn is_element(&self) -> bool {
do self.with_imm_base |base| {
base.node.is_element()
}
}
/// A convenience function to access the computed style of the DOM node that this render box
/// represents.
fn style(&self) -> CompleteStyle {
self.with_imm_base(|base| base.node.style())
self.with_base(|base| base.node.style())
}
/// A convenience function to access the DOM node that this render box represents.
fn node(&self) -> AbstractNode<LayoutView> {
self.with_imm_base(|base| base.node)
self.with_base(|base| base.node)
}
/// Returns the nearest ancestor-or-self `Element` to the DOM node that this render box
@ -512,7 +511,7 @@ pub impl RenderBox {
///
/// If there is no ancestor-or-self `Element` node, fails.
fn nearest_ancestor_element(&self) -> AbstractNode<LayoutView> {
do self.with_imm_base |base| {
do self.with_base |base| {
let mut node = base.node;
while !node.is_element() {
match node.parent_node() {
@ -542,11 +541,11 @@ pub impl RenderBox {
/// representing the box's stacking context. When asked to construct its constituent display
/// items, each box puts its display items into the correct stack layer according to CSS 2.1
/// Appendix E. Finally, the builder flattens the list.
fn build_display_list(&self,
_: &DisplayListBuilder,
dirty: &Rect<Au>,
offset: &Point2D<Au>,
list: &Cell<DisplayList>) {
fn build_display_list<E:ExtraDisplayListData>(&self,
_: &DisplayListBuilder,
dirty: &Rect<Au>,
offset: &Point2D<Au>,
list: &Cell<DisplayList<E>>) {
let box_bounds = self.position();
let absolute_box_bounds = box_bounds.translate(offset);
debug!("RenderBox::build_display_list at rel=%?, abs=%?: %s",
@ -574,6 +573,7 @@ pub impl RenderBox {
let text_display_item = ~TextDisplayItem {
base: BaseDisplayItem {
bounds: absolute_box_bounds,
extra: ExtraDisplayListData::new(*self),
},
// FIXME(pcwalton): Allocation? Why?!
text_run: ~text_box.run.serialize(),
@ -594,6 +594,7 @@ pub impl RenderBox {
let border_display_item = ~BorderDisplayItem {
base: BaseDisplayItem {
bounds: absolute_box_bounds,
extra: ExtraDisplayListData::new(*self),
},
width: Au::from_px(1),
color: rgb(0, 0, 200).to_gfx_color(),
@ -613,6 +614,7 @@ pub impl RenderBox {
let border_display_item = ~BorderDisplayItem {
base: BaseDisplayItem {
bounds: baseline,
extra: ExtraDisplayListData::new(*self),
},
width: Au::from_px(1),
color: rgb(0, 200, 0).to_gfx_color(),
@ -630,7 +632,8 @@ pub impl RenderBox {
do list.with_mut_ref |list| {
let border_display_item = ~BorderDisplayItem {
base: BaseDisplayItem {
bounds: absolute_box_bounds,
bounds: absolute_box_bounds,
extra: ExtraDisplayListData::new(*self),
},
width: Au::from_px(1),
color: rgb(0, 0, 0).to_gfx_color(),
@ -650,6 +653,7 @@ pub impl RenderBox {
let image_display_item = ~ImageDisplayItem {
base: BaseDisplayItem {
bounds: absolute_box_bounds,
extra: ExtraDisplayListData::new(*self),
},
image: image.clone(),
};
@ -674,9 +678,9 @@ pub impl RenderBox {
/// Adds the display items necessary to paint the background of this render box to the display
/// list if necessary.
fn paint_background_if_applicable(&self,
list: &Cell<DisplayList>,
absolute_bounds: &Rect<Au>) {
fn paint_background_if_applicable<E:ExtraDisplayListData>(&self,
list: &Cell<DisplayList<E>>,
absolute_bounds: &Rect<Au>) {
// FIXME: This causes a lot of background colors to be displayed when they are clearly not
// needed. We could use display list optimization to clean this up, but it still seems
// inefficient. What we really want is something like "nearest ancestor element that
@ -689,6 +693,7 @@ pub impl RenderBox {
let solid_color_display_item = ~SolidColorDisplayItem {
base: BaseDisplayItem {
bounds: *absolute_bounds,
extra: ExtraDisplayListData::new(*self),
},
color: background_color.to_gfx_color(),
};
@ -698,70 +703,6 @@ pub impl RenderBox {
}
}
/// Adds the display items necessary to paint the borders of this render box to the display
/// list if necessary.
fn paint_borders_if_applicable(&self, list: &Cell<DisplayList>, abs_bounds: &Rect<Au>) {
if !self.is_element() {
return
}
let style = self.style();
let (top_width, right_width) = (style.border_top_width(), style.border_right_width());
let (bottom_width, left_width) = (style.border_bottom_width(), style.border_left_width());
match (top_width, right_width, bottom_width, left_width) {
(CSSBorderWidthLength(Px(top)),
CSSBorderWidthLength(Px(right)),
CSSBorderWidthLength(Px(bottom)),
CSSBorderWidthLength(Px(left))) => {
let top_au = Au::from_frac_px(top);
let right_au = Au::from_frac_px(right);
let bottom_au = Au::from_frac_px(bottom);
let left_au = Au::from_frac_px(left);
// Are all the widths equal?
if [ top_au, right_au, bottom_au ].all(|a| *a == left_au) {
let border_width = top_au;
let bounds = Rect {
origin: Point2D {
x: abs_bounds.origin.x - border_width / Au(2),
y: abs_bounds.origin.y - border_width / Au(2),
},
size: Size2D {
width: abs_bounds.size.width + border_width,
height: abs_bounds.size.height + border_width
}
};
let top_color = self.style().border_top_color();
let color = top_color.to_gfx_color(); // FIXME
// Append the border to the display list.
do list.with_mut_ref |list| {
let border_display_item = ~BorderDisplayItem {
base: BaseDisplayItem {
bounds: bounds,
},
width: border_width,
color: color,
};
list.append_item(BorderDisplayItemClass(border_display_item))
}
} else {
warn!("ignoring unimplemented border widths");
}
}
(CSSBorderWidthMedium,
CSSBorderWidthMedium,
CSSBorderWidthMedium,
CSSBorderWidthMedium) => {
// FIXME: This seems to be the default for non-root nodes. For now we'll ignore it.
warn!("ignoring medium border widths");
}
_ => warn!("ignoring unimplemented border widths")
}
}
/// Converts this node's computed style to a font style used for rendering.
fn font_style(&self) -> FontStyle {
let my_style = self.nearest_ancestor_element().style();
@ -884,3 +825,4 @@ pub impl RenderBox {
}
}

View file

@ -183,7 +183,10 @@ impl BoxGenerator {
}
let mut node_range: Range = Range::new(self.range_stack.pop(), 0);
node_range.extend_to(inline.boxes.len());
assert!(node_range.length() > 0);
if node_range.length() == 0 {
warn!("node range length is zero?!")
}
debug!("BoxGenerator: adding element range=%?", node_range);
inline.elems.add_mapping(node, &node_range);
@ -444,47 +447,49 @@ pub impl LayoutTreeBuilder {
// FIXME: this will create refcounted cycles between the removed flow and any
// of its RenderBox or FlowContext children, and possibly keep alive other junk
let parent_flow = *parent_flow;
let (first_child, last_child) = do parent_flow.with_base |parent_node| {
(parent_node.first_child, parent_node.last_child)
};
// check first/last child for whitespace-ness
let first_child = do parent_flow.with_base |parent_node| {
parent_node.first_child
};
for first_child.each |first_flow| {
if first_flow.starts_inline_flow() {
// FIXME: workaround for rust#6393
let mut do_remove = false;
{
let boxes = &first_flow.inline().boxes;
if boxes.len() == 1 && boxes[0].is_whitespace_only() {
debug!("LayoutTreeBuilder: pruning whitespace-only first child flow \
f%d from parent f%d",
first_flow.id(),
parent_flow.id());
do_remove = true;
}
let boxes = &first_flow.inline().boxes;
if boxes.len() == 1 && boxes[0].is_whitespace_only() {
debug!("LayoutTreeBuilder: pruning whitespace-only first child \
flow f%d from parent f%d",
first_flow.id(),
parent_flow.id());
do_remove = true;
}
}
if (do_remove) {
parent_flow.remove_child(*first_flow);
(*parent_flow).remove_child(*first_flow);
}
}
}
let last_child = do parent_flow.with_base |parent_node| {
parent_node.last_child
};
for last_child.each |last_flow| {
if last_flow.starts_inline_flow() {
// FIXME: workaround for rust#6393
let mut do_remove = false;
{
let boxes = &last_flow.inline().boxes;
if boxes.len() == 1 && boxes.last().is_whitespace_only() {
debug!("LayoutTreeBuilder: pruning whitespace-only last child flow \
f%d from parent f%d",
last_flow.id(),
parent_flow.id());
do_remove = true;
}
let boxes = &last_flow.inline().boxes;
if boxes.len() == 1 && boxes.last().is_whitespace_only() {
debug!("LayoutTreeBuilder: pruning whitespace-only last child \
flow f%d from parent f%d",
last_flow.id(),
parent_flow.id());
do_remove = true;
}
}
if (do_remove) {
parent_flow.remove_child(*last_flow);
(*parent_flow).remove_child(*last_flow);
}
}
}

View file

@ -8,12 +8,10 @@ use geom::rect::Rect;
use gfx::font_context::FontContext;
use gfx::geometry::Au;
use servo_net::local_image_cache::LocalImageCache;
use std::net::url::Url;
/// Data needed by the layout task.
pub struct LayoutContext {
font_ctx: @mut FontContext,
image_cache: @mut LocalImageCache,
doc_url: Url,
screen_size: Rect<Au>
}

View file

@ -2,15 +2,13 @@
* 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/. */
///
/// Constructs display lists from render boxes.
///
use core::cell::Cell;
//! Constructs display lists from render boxes.
use layout::box::RenderBox;
use layout::context::LayoutContext;
use layout::flow::FlowContext;
use core::cell::Cell;
use geom::point::Point2D;
use geom::rect::Rect;
use gfx::display_list::DisplayList;
@ -19,6 +17,28 @@ use gfx;
use newcss;
use servo_util::tree::TreeNodeRef;
/// Extra display list data is either nothing (if the display list is to be rendered) or the
/// originating render box (if the display list is generated for hit testing).
pub trait ExtraDisplayListData {
fn new(box: RenderBox) -> Self;
}
/// The type representing the lack of extra display list data. This is used when sending display
/// list data off to be rendered.
pub type Nothing = ();
impl ExtraDisplayListData for Nothing {
fn new(_: RenderBox) -> Nothing {
()
}
}
impl ExtraDisplayListData for RenderBox {
fn new(box: RenderBox) -> RenderBox {
box
}
}
/// A builder object that manages display list builder should mainly hold information about the
/// initial request and desired result--for example, whether the `DisplayList` is to be used for
/// painting or hit testing. This can affect which boxes are created.
@ -30,30 +50,33 @@ pub struct DisplayListBuilder<'self> {
}
pub trait FlowDisplayListBuilderMethods {
fn build_display_list(&self, a: &DisplayListBuilder, b: &Rect<Au>, c: &Cell<DisplayList>);
fn build_display_list_for_child(&self,
a: &DisplayListBuilder,
b: FlowContext,
c: &Rect<Au>,
d: &Point2D<Au>,
e: &Cell<DisplayList>);
fn build_display_list<E:ExtraDisplayListData>(&self,
a: &DisplayListBuilder,
b: &Rect<Au>,
c: &Cell<DisplayList<E>>);
fn build_display_list_for_child<E:ExtraDisplayListData>(&self,
a: &DisplayListBuilder,
b: FlowContext,
c: &Rect<Au>,
d: &Point2D<Au>,
e: &Cell<DisplayList<E>>);
}
impl FlowDisplayListBuilderMethods for FlowContext {
fn build_display_list(&self,
builder: &DisplayListBuilder,
dirty: &Rect<Au>,
list: &Cell<DisplayList>) {
fn build_display_list<E:ExtraDisplayListData>(&self,
builder: &DisplayListBuilder,
dirty: &Rect<Au>,
list: &Cell<DisplayList<E>>) {
let zero = gfx::geometry::zero_point();
self.build_display_list_recurse(builder, dirty, &zero, list);
}
fn build_display_list_for_child(&self,
builder: &DisplayListBuilder,
child_flow: FlowContext,
dirty: &Rect<Au>,
offset: &Point2D<Au>,
list: &Cell<DisplayList>) {
fn build_display_list_for_child<E:ExtraDisplayListData>(&self,
builder: &DisplayListBuilder,
child_flow: FlowContext,
dirty: &Rect<Au>,
offset: &Point2D<Au>,
list: &Cell<DisplayList<E>>) {
// Adjust the dirty rect to child flow context coordinates.
do child_flow.with_base |child_node| {
let abs_flow_bounds = child_node.position.translate(offset);

View file

@ -28,7 +28,7 @@
use layout::block::BlockFlowData;
use layout::box::RenderBox;
use layout::context::LayoutContext;
use layout::display_list_builder::DisplayListBuilder;
use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData};
use layout::inline::{InlineFlowData};
use core::cell::Cell;
@ -284,11 +284,11 @@ impl<'self> FlowContext {
}
}
pub fn build_display_list_recurse(&self,
builder: &DisplayListBuilder,
dirty: &Rect<Au>,
offset: &Point2D<Au>,
list: &Cell<DisplayList>) {
pub fn build_display_list_recurse<E:ExtraDisplayListData>(&self,
builder: &DisplayListBuilder,
dirty: &Rect<Au>,
offset: &Point2D<Au>,
list: &Cell<DisplayList<E>>) {
do self.with_base |info| {
debug!("FlowContext::build_display_list at %?: %s", info.position, self.debug_str());
}

View file

@ -7,7 +7,7 @@ use core;
use layout::box::{CannotSplit, GenericRenderBoxClass, ImageRenderBoxClass, RenderBox};
use layout::box::{SplitDidFit, SplitDidNotFit, TextRenderBoxClass, UnscannedTextRenderBoxClass};
use layout::context::LayoutContext;
use layout::display_list_builder::DisplayListBuilder;
use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData};
use layout::flow::{FlowContext, FlowData, InlineFlow};
use layout::text::{UnscannedMethods, adapt_textbox_with_range};
@ -141,8 +141,8 @@ impl ElementMapping {
// XXX: the following loop form causes segfaults; assigning to locals doesn't.
// while new_j < new_boxes.len() && old_boxes[old_i].d().node != new_boxes[new_j].d().node {
while new_j < new_boxes.len() {
let should_leave = do old_boxes[old_i].with_imm_base |old_box_base| {
do new_boxes[new_j].with_imm_base |new_box_base| {
let should_leave = do old_boxes[old_i].with_base |old_box_base| {
do new_boxes[new_j].with_base |new_box_base| {
old_box_base.node != new_box_base.node
}
};
@ -284,7 +284,7 @@ impl TextRunScanner {
let run = @fontgroup.create_textrun(transformed_text, underline);
debug!("TextRunScanner: pushing single text box in range: %?", self.clump);
let new_box = do old_box.with_imm_base |old_box_base| {
let new_box = do old_box.with_base |old_box_base| {
let range = Range::new(0, run.char_len());
@mut adapt_textbox_with_range(*old_box_base, run, range)
};
@ -345,7 +345,7 @@ impl TextRunScanner {
loop
}
do in_boxes[i].with_imm_base |base| {
do in_boxes[i].with_base |base| {
let new_box = @mut adapt_textbox_with_range(*base, run.get(), range);
out_boxes.push(TextRenderBoxClass(new_box));
}
@ -864,11 +864,11 @@ impl InlineFlowData {
self.common.position.size.height = cur_y;
}
pub fn build_display_list_inline(&self,
builder: &DisplayListBuilder,
dirty: &Rect<Au>,
offset: &Point2D<Au>,
list: &Cell<DisplayList>) {
pub fn build_display_list_inline<E:ExtraDisplayListData>(&self,
builder: &DisplayListBuilder,
dirty: &Rect<Au>,
offset: &Point2D<Au>,
list: &Cell<DisplayList<E>>) {
// TODO(#228): Once we form line boxes and have their cached bounds, we can be smarter and
// not recurse on a line if nothing in it can intersect the dirty region.
debug!("FlowContext[%d]: building display list for %u inline boxes",

View file

@ -8,15 +8,12 @@
use css::matching::MatchMethods;
use css::select::new_css_select_ctx;
use layout::aux::{LayoutData, LayoutAuxMethods};
use layout::box::RenderBox;
use layout::box_builder::LayoutTreeBuilder;
use layout::context::LayoutContext;
use layout::display_list_builder::{DisplayListBuilder, FlowDisplayListBuilderMethods};
use layout::flow::FlowContext;
use util::task::spawn_listener;
use servo_util::time;
use servo_util::time::time;
use servo_util::time::profile;
use servo_util::time::ProfilerChan;
use core::cast::transmute;
use core::cell::Cell;
@ -35,14 +32,19 @@ use newcss::stylesheet::Stylesheet;
use newcss::types::OriginAuthor;
use script::dom::event::ReflowEvent;
use script::dom::node::{AbstractNode, LayoutView};
use script::layout_interface::{AddStylesheetMsg, BuildData, BuildMsg, ContentBoxQuery};
use script::layout_interface::{ContentBoxResponse, ContentBoxesQuery, ContentBoxesResponse};
use script::layout_interface::{ExitMsg, LayoutQuery, LayoutResponse, LayoutTask};
use script::layout_interface::{MatchSelectorsDamage, Msg, NoDamage, QueryMsg, ReflowDamage};
use script::layout_interface::{AddStylesheetMsg, ContentBoxQuery};
use script::layout_interface::{HitTestQuery, ContentBoxResponse, HitTestResponse};
use script::layout_interface::{ContentBoxesQuery, ContentBoxesResponse, ExitMsg, LayoutQuery};
use script::layout_interface::{LayoutResponse, LayoutTask, MatchSelectorsDocumentDamage, Msg};
use script::layout_interface::{QueryMsg, Reflow, ReflowDocumentDamage, ReflowForDisplay};
use script::layout_interface::{ReflowMsg};
use script::script_task::{ScriptMsg, SendEventMsg};
use servo_net::image_cache_task::{ImageCacheTask, ImageResponseMsg};
use servo_net::local_image_cache::LocalImageCache;
use servo_util::tree::TreeUtils;
use servo_util::tree::{TreeNodeRef, TreeUtils};
use servo_util::time::{ProfilerChan, profile, time};
use servo_util::time;
use std::net::url::Url;
pub fn create_layout_task(render_task: RenderTask,
img_cache_task: ImageCacheTask,
@ -69,6 +71,8 @@ struct Layout {
local_image_cache: @mut LocalImageCache,
from_script: Port<Msg>,
font_ctx: @mut FontContext,
doc_url: Option<Url>,
screen_size: Option<Size2D<Au>>,
/// This is used to root reader data.
layout_refs: ~[@mut LayoutData],
@ -92,6 +96,9 @@ impl Layout {
local_image_cache: @mut LocalImageCache(image_cache_task),
from_script: from_script,
font_ctx: fctx,
doc_url: None,
screen_size: None,
layout_refs: ~[],
css_select_ctx: @mut new_css_select_ctx(),
profiler_chan: profiler_chan,
@ -104,14 +111,27 @@ impl Layout {
}
}
// Create a layout context for use in building display lists, hit testing, &c.
fn build_layout_context(&self) -> LayoutContext {
let image_cache = self.local_image_cache;
let font_ctx = self.font_ctx;
let screen_size = self.screen_size.unwrap();
LayoutContext {
image_cache: image_cache,
font_ctx: font_ctx,
screen_size: Rect(Point2D(Au(0), Au(0)), screen_size),
}
}
fn handle_request(&mut self) -> bool {
match self.from_script.recv() {
AddStylesheetMsg(sheet) => self.handle_add_stylesheet(sheet),
BuildMsg(data) => {
ReflowMsg(data) => {
let data = Cell(data);
do profile(time::LayoutPerformCategory, self.profiler_chan.clone()) {
self.handle_build(data.take());
self.handle_reflow(data.take());
}
}
QueryMsg(query, chan) => {
@ -135,10 +155,10 @@ impl Layout {
}
/// The high-level routine that performs layout tasks.
fn handle_build(&mut self, data: &BuildData) {
fn handle_reflow(&mut self, data: &Reflow) {
// FIXME: Isolate this transmutation into a "bridge" module.
let node: &AbstractNode<LayoutView> = unsafe {
transmute(&data.node)
transmute(&data.document_root)
};
// FIXME: Bad copy!
@ -149,20 +169,16 @@ impl Layout {
debug!("layout: damage is %?", data.damage);
debug!("layout: parsed Node tree");
debug!("%?", node.dump());
// Reset the image cache.
self.local_image_cache.next_round(self.make_on_image_available_cb(script_chan));
self.doc_url = Some(doc_url);
let screen_size = Size2D(Au::from_px(data.window_size.width as int),
Au::from_px(data.window_size.height as int));
self.screen_size = Some(screen_size);
// Create a layout context for use throughout the following passes.
let mut layout_ctx = LayoutContext {
image_cache: self.local_image_cache,
font_ctx: self.font_ctx,
doc_url: doc_url,
screen_size: Rect(Point2D(Au(0), Au(0)), screen_size)
};
let mut layout_ctx = self.build_layout_context();
// Initialize layout data for each node.
//
@ -172,9 +188,9 @@ impl Layout {
}
// Perform CSS selector matching if necessary.
match data.damage {
NoDamage | ReflowDamage => {}
MatchSelectorsDamage => {
match data.damage.level {
ReflowDocumentDamage => {}
MatchSelectorsDocumentDamage => {
do profile(time::LayoutSelectorMatchCategory, self.profiler_chan.clone()) {
node.restyle_subtree(self.css_select_ctx);
}
@ -210,25 +226,31 @@ impl Layout {
};
}
// Build the display list, and send it to the renderer.
do profile(time::LayoutDispListBuildCategory, self.profiler_chan.clone()) {
let builder = DisplayListBuilder {
ctx: &layout_ctx,
};
// Build the display list if necessary, and send it to the renderer.
if data.goal == ReflowForDisplay {
do profile(time::LayoutDispListBuildCategory, self.profiler_chan.clone()) {
let builder = DisplayListBuilder {
ctx: &layout_ctx,
};
let display_list = @Cell(DisplayList::new());
// TODO: Set options on the builder before building.
// TODO: Be smarter about what needs painting.
layout_root.build_display_list(&builder, &layout_root.position(), display_list);
let display_list = @Cell(DisplayList::new());
let render_layer = RenderLayer {
display_list: display_list.take(),
size: Size2D(screen_size.width.to_px() as uint, screen_size.height.to_px() as uint)
};
// TODO: Set options on the builder before building.
// TODO: Be smarter about what needs painting.
layout_root.build_display_list(&builder, &layout_root.position(), display_list);
self.render_task.channel.send(RenderMsg(render_layer));
} // time(layout: display list building)
let root_size = do layout_root.with_base |base| {
base.position.size
};
let render_layer = RenderLayer {
display_list: display_list.take(),
size: Size2D(root_size.width.to_px() as uint, root_size.height.to_px() as uint)
};
self.render_task.channel.send(RenderMsg(render_layer));
} // time(layout: display list building)
}
debug!("%?", layout_root.dump());
@ -290,6 +312,55 @@ impl Layout {
}
};
reply_chan.send(response)
}
HitTestQuery(node, point) => {
// FIXME: Isolate this transmutation into a single "bridge" module.
let node: AbstractNode<LayoutView> = unsafe {
transmute(node)
};
let mut flow_node: AbstractNode<LayoutView> = node;
for node.traverse_preorder |node| {
if node.layout_data().flow.is_some() {
flow_node = node;
break;
}
};
let response = match flow_node.layout_data().flow {
None => {
debug!("HitTestQuery: flow is None");
Err(())
}
Some(flow) => {
let layout_ctx = self.build_layout_context();
let builder = DisplayListBuilder {
ctx: &layout_ctx,
};
let display_list: @Cell<DisplayList<RenderBox>> =
@Cell(DisplayList::new());
flow.build_display_list(&builder,
&flow.position(),
display_list);
// iterate in reverse to ensure we have the most recently painted render box
let (x, y) = (Au::from_frac_px(point.x as float),
Au::from_frac_px(point.y as float));
let mut resp = Err(());
let display_list = &display_list.take().list;
for display_list.each_reverse |display_item| {
let bounds = display_item.bounds();
if x <= bounds.origin.x + bounds.size.width &&
bounds.origin.x <= x &&
y < bounds.origin.y + bounds.size.height &&
bounds.origin.y < y {
resp = Ok(HitTestResponse(display_item.base().extra.node()));
break;
}
}
resp
}
};
reply_chan.send(response)
}
}

View file

@ -0,0 +1,199 @@
/* 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/. */
//! Borders, padding, and margins.
use layout::display_list_builder::{ExtraDisplayListData, ToGfxColor};
use layout::box::RenderBox;
use core::cell::Cell;
use core::num::Zero;
use geom::point::Point2D;
use geom::rect::Rect;
use geom::size::Size2D;
use geom::side_offsets::SideOffsets2D;
use gfx::display_list::{BaseDisplayItem, BorderDisplayItem, BorderDisplayItemClass, DisplayList};
use gfx::geometry::Au;
use newcss::complete::CompleteStyle;
use newcss::units::{Em, Pt, Px};
use newcss::values::{CSSBorderWidth, CSSBorderWidthLength, CSSBorderWidthMedium};
use newcss::values::{CSSBorderWidthThick, CSSBorderWidthThin};
use newcss::values::{CSSWidth, CSSWidthLength, CSSWidthPercentage, CSSWidthAuto};
use newcss::values::{CSSMargin, CSSMarginLength, CSSMarginPercentage, CSSMarginAuto};
use newcss::values::{CSSPadding, CSSPaddingLength, CSSPaddingPercentage};
/// Encapsulates the borders, padding, and margins, which we collectively call the "box model".
pub struct BoxModel {
border: SideOffsets2D<Au>,
padding: SideOffsets2D<Au>,
margin: SideOffsets2D<Au>,
/// The width of the content box.
content_box_width: Au,
}
/// Useful helper data type when computing values for blocks and positioned elements.
pub enum MaybeAuto {
Auto,
Specified(Au),
}
impl MaybeAuto {
pub fn from_margin(margin: CSSMargin) -> MaybeAuto{
match margin {
CSSMarginAuto => Auto,
//FIXME(eatkinson): Compute percents properly
CSSMarginPercentage(_) => Specified(Au(0)),
//FIXME(eatkinson): Compute pt and em values properly
CSSMarginLength(Px(v)) |
CSSMarginLength(Pt(v)) |
CSSMarginLength(Em(v)) => Specified(Au::from_frac_px(v)),
}
}
pub fn from_width(width: CSSWidth) -> MaybeAuto{
match width{
CSSWidthAuto => Auto,
//FIXME(eatkinson): Compute percents properly
CSSWidthPercentage(_) => Specified(Au(0)),
//FIXME(eatkinson): Compute pt and em values properly
CSSWidthLength(Px(v)) |
CSSWidthLength(Pt(v)) |
CSSWidthLength(Em(v)) => Specified(Au::from_frac_px(v)),
}
}
pub fn spec_or_default(&self, default: Au) -> Au{
match *self{
Auto => default,
Specified(value) => value
}
}
}
impl Zero for BoxModel {
fn zero() -> BoxModel {
BoxModel {
border: Zero::zero(),
padding: Zero::zero(),
margin: Zero::zero(),
content_box_width: Zero::zero(),
}
}
fn is_zero(&self) -> bool {
self.padding.is_zero() && self.border.is_zero() && self.margin.is_zero()
}
}
impl BoxModel {
/// Populates the box model parameters from the given computed style.
pub fn compute_borders(&mut self, style: CompleteStyle) {
// Compute the borders.
self.border.top = self.compute_border_width(style.border_top_width());
self.border.right = self.compute_border_width(style.border_right_width());
self.border.bottom = self.compute_border_width(style.border_bottom_width());
self.border.left = self.compute_border_width(style.border_left_width());
}
pub fn compute_padding(&mut self, style: CompleteStyle, cb_width: Au){
self.padding.top = self.compute_padding_length(style.padding_top(), cb_width);
self.padding.right = self.compute_padding_length(style.padding_right(), cb_width);
self.padding.bottom = self.compute_padding_length(style.padding_bottom(), cb_width);
self.padding.left = self.compute_padding_length(style.padding_left(), cb_width);
}
pub fn noncontent_width(&self) -> Au {
let left = self.margin.left + self.border.left + self.padding.left;
let right = self.margin.right + self.border.right + self.padding.right;
left + right
}
pub fn offset(&self) -> Au {
self.margin.left + self.border.left + self.padding.left
}
/// Helper function to compute the border width in app units from the CSS border width.
priv fn compute_border_width(&self, width: CSSBorderWidth) -> Au {
match width {
CSSBorderWidthLength(Px(v)) |
CSSBorderWidthLength(Em(v)) |
CSSBorderWidthLength(Pt(v)) => {
// FIXME(pcwalton): Handle `em` and `pt` correctly.
Au::from_frac_px(v)
}
CSSBorderWidthThin => Au::from_px(1),
CSSBorderWidthMedium => Au::from_px(5),
CSSBorderWidthThick => Au::from_px(10),
}
}
fn compute_padding_length(&self, padding: CSSPadding, content_box_width: Au) -> Au {
match padding {
CSSPaddingLength(Px(v)) |
CSSPaddingLength(Pt(v)) |
CSSPaddingLength(Em(v)) => {
// FIXME(eatkinson): Handle 'em' and 'pt' correctly
Au::from_frac_px(v)
}
CSSPaddingPercentage(p) => content_box_width.scale_by(p)
}
}
}
//
// Painting
//
impl RenderBox {
/// Adds the display items necessary to paint the borders of this render box to a display list
/// if necessary.
pub fn paint_borders_if_applicable<E:ExtraDisplayListData>(&self,
list: &Cell<DisplayList<E>>,
abs_bounds: &Rect<Au>) {
// Fast path.
let border = do self.with_base |base| {
base.model.border
};
if border.is_zero() {
return
}
// Are all the widths equal?
//
// FIXME(pcwalton): Obviously this is wrong.
if [ border.top, border.right, border.bottom ].all(|a| *a == border.left) {
let border_width = border.top;
let bounds = Rect {
origin: Point2D {
x: abs_bounds.origin.x,
y: abs_bounds.origin.y,
},
size: Size2D {
width: abs_bounds.size.width,
height: abs_bounds.size.height
}
};
let top_color = self.style().border_top_color();
let color = top_color.to_gfx_color(); // FIXME
// Append the border to the display list.
do list.with_mut_ref |list| {
let border_display_item = ~BorderDisplayItem {
base: BaseDisplayItem {
bounds: bounds,
extra: ExtraDisplayListData::new(*self),
},
width: border_width,
color: color,
};
list.append_item(BorderDisplayItemClass(border_display_item))
}
} else {
warn!("ignoring unimplemented border widths");
}
}
}

View file

@ -7,8 +7,8 @@
/// GLUT is a very old and bare-bones toolkit. However, it has good cross-platform support, at
/// least on desktops. It is designed for testing Servo without the need of a UI.
use windowing::{ApplicationMethods, CompositeCallback, LoadUrlCallback, ResizeCallback};
use windowing::{ScrollCallback, WindowMethods};
use windowing::{ApplicationMethods, CompositeCallback, LoadUrlCallback, ClickCallback};
use windowing::{ResizeCallback, ScrollCallback, WindowMethods};
use alert::{Alert, AlertMethods};
use core::libc::c_int;
@ -16,6 +16,7 @@ use geom::point::Point2D;
use geom::size::Size2D;
use glut::glut::{DOUBLE, WindowHeight, WindowWidth};
use glut::glut;
use glut::machack;
/// A structure responsible for setting up and tearing down the entire windowing system.
pub struct Application;
@ -35,6 +36,7 @@ pub struct Window {
composite_callback: Option<CompositeCallback>,
resize_callback: Option<ResizeCallback>,
load_url_callback: Option<LoadUrlCallback>,
click_callback: Option<ClickCallback>,
scroll_callback: Option<ScrollCallback>,
drag_origin: Point2D<c_int>,
@ -54,6 +56,7 @@ impl WindowMethods<Application> for Window {
composite_callback: None,
resize_callback: None,
load_url_callback: None,
click_callback: None,
scroll_callback: None,
drag_origin: Point2D(0, 0),
@ -76,13 +79,16 @@ impl WindowMethods<Application> for Window {
do glut::keyboard_func |key, _, _| {
window.handle_key(key)
}
do glut::mouse_func |_, _, x, y| {
window.start_drag(x, y)
}
do glut::motion_func |x, y| {
window.continue_drag(x, y)
do glut::mouse_func |button, _, x, y| {
if button < 3 {
window.handle_click(x, y);
} else {
window.handle_scroll(if button == 4 { -30.0 } else { 30.0 });
}
}
machack::perform_scroll_wheel_hack();
window
}
@ -94,7 +100,6 @@ impl WindowMethods<Application> for Window {
/// Presents the window to the screen (perhaps by page flipping).
pub fn present(&mut self) {
glut::swap_buffers();
glut::post_redisplay();
}
/// Registers a callback to run when a composite event occurs.
@ -112,6 +117,11 @@ impl WindowMethods<Application> for Window {
self.load_url_callback = Some(new_load_url_callback)
}
/// Registers a callback to be run when a click event occurs.
pub fn set_click_callback(&mut self, new_click_callback: ClickCallback) {
self.click_callback = Some(new_click_callback)
}
/// Registers a callback to be run when the user scrolls.
pub fn set_scroll_callback(&mut self, new_scroll_callback: ScrollCallback) {
self.scroll_callback = Some(new_scroll_callback)
@ -137,20 +147,19 @@ impl Window {
}
}
/// Helper function to start a drag.
fn start_drag(&mut self, x: c_int, y: c_int) {
self.drag_origin = Point2D(x, y)
/// Helper function to handle a click
fn handle_click(&self, x: c_int, y: c_int) {
match self.click_callback {
None => {}
Some(callback) => callback(Point2D(x as f32, y as f32)),
}
}
/// Helper function to continue a drag.
fn continue_drag(&mut self, x: c_int, y: c_int) {
let new_point = Point2D(x, y);
let delta = new_point - self.drag_origin;
self.drag_origin = new_point;
/// Helper function to handle a scroll.
fn handle_scroll(&mut self, delta: f32) {
match self.scroll_callback {
None => {}
Some(callback) => callback(Point2D(delta.x as f32, delta.y as f32)),
Some(callback) => callback(Point2D(0.0, delta)),
}
}

View file

@ -68,6 +68,7 @@ pub mod layout {
pub mod flow;
pub mod layout_task;
pub mod inline;
pub mod model;
pub mod text;
mod aux;
}

View file

@ -16,6 +16,10 @@ pub type ResizeCallback = @fn(uint, uint);
/// Type of the function that is called when a new URL is to be loaded.
pub type LoadUrlCallback = @fn(&str);
/// Type of the function that is called when hit testing is to be performed.
/// FIXME this currently does not discriminate between left and right clicks or any modifiers
pub type ClickCallback = @fn(Point2D<f32>);
/// Type of the function that is called when the user scrolls.
pub type ScrollCallback = @fn(Point2D<f32>);
@ -38,6 +42,8 @@ pub trait WindowMethods<A> {
pub fn set_resize_callback(&mut self, new_resize_callback: ResizeCallback);
/// Registers a callback to run when a new URL is to be loaded.
pub fn set_load_url_callback(&mut self, new_load_url_callback: LoadUrlCallback);
/// Registers a callback to run when the user clicks.
pub fn set_click_callback(&mut self, new_click_callback: ClickCallback);
/// Registers a callback to run when the user scrolls.
pub fn set_scroll_callback(&mut self, new_scroll_callback: ScrollCallback);

View file

@ -7,9 +7,12 @@ use dom::window::Window;
use dom::bindings::codegen::EventBinding;
use dom::bindings::utils::{DOMString, ErrorResult, WrapperCache};
use geom::point::Point2D;
pub enum Event {
ResizeEvent(uint, uint, comm::Chan<()>),
ReflowEvent
ReflowEvent,
ClickEvent(Point2D<f32>),
}
pub struct Event_ {

View file

@ -4,7 +4,7 @@
use dom::bindings::utils::WrapperCache;
use dom::bindings::window;
use layout_interface::MatchSelectorsDamage;
use layout_interface::ReflowForScriptQuery;
use script_task::{ExitMsg, FireTimerMsg, ScriptMsg, ScriptContext};
use core::comm::{Chan, SharedChan};
@ -83,7 +83,7 @@ pub impl Window {
fn content_changed(&self) {
unsafe {
(*self.script_context).trigger_relayout(MatchSelectorsDamage);
(*self.script_context).reflow_all(ReflowForScriptQuery)
}
}

View file

@ -6,12 +6,13 @@
/// coupling between these two components, and enables the DOM to be placed in a separate crate
/// from layout.
use dom::node::{AbstractNode, ScriptView};
use dom::node::{AbstractNode, ScriptView, LayoutView};
use script_task::ScriptMsg;
use core::comm::{Chan, SharedChan};
use geom::rect::Rect;
use geom::size::Size2D;
use geom::point::Point2D;
use gfx::geometry::Au;
use newcss::stylesheet::Stylesheet;
use std::net::url::Url;
@ -24,9 +25,7 @@ pub enum Msg {
AddStylesheetMsg(Stylesheet),
/// Requests a reflow.
///
/// FIXME(pcwalton): Call this `reflow` instead?
BuildMsg(~BuildData),
ReflowMsg(~Reflow),
/// Performs a synchronous layout request.
///
@ -43,6 +42,8 @@ pub enum LayoutQuery {
ContentBoxQuery(AbstractNode<ScriptView>),
/// Requests the dimensions of all the content boxes, as in the `getClientRects()` call.
ContentBoxesQuery(AbstractNode<ScriptView>),
/// Requests the node containing the point of interest
HitTestQuery(AbstractNode<ScriptView>, Point2D<f32>),
}
/// The reply of a synchronous message from script to layout.
@ -54,44 +55,65 @@ pub enum LayoutResponse {
ContentBoxResponse(Rect<Au>),
/// A response to the `ContentBoxesQuery` message.
ContentBoxesResponse(~[Rect<Au>]),
/// A response to the `HitTestQuery` message.
HitTestResponse(AbstractNode<LayoutView>),
}
/// 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,
/// Determines which part of the
pub enum DocumentDamageLevel {
/// Perform CSS selector matching and reflow.
MatchSelectorsDamage,
MatchSelectorsDocumentDamage,
/// Reflow, but do not perform CSS selector matching.
ReflowDocumentDamage,
}
impl Damage {
impl DocumentDamageLevel {
/// 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) {
fn add(&mut self, new_damage: DocumentDamageLevel) {
match (*self, new_damage) {
(NoDamage, _) => *self = new_damage,
(ReflowDamage, NoDamage) => *self = ReflowDamage,
(ReflowDamage, new_damage) => *self = new_damage,
(MatchSelectorsDamage, _) => *self = MatchSelectorsDamage
(ReflowDocumentDamage, new_damage) => *self = new_damage,
(MatchSelectorsDocumentDamage, _) => *self = MatchSelectorsDocumentDamage,
}
}
}
/// What parts of the document have changed, as far as the script task can tell.
///
/// Note that this is fairly coarse-grained and is separate from layout's notion of the document
pub struct DocumentDamage {
/// The topmost node in the tree that has changed.
root: AbstractNode<ScriptView>,
/// The amount of damage that occurred.
level: DocumentDamageLevel,
}
/// Why we're doing reflow.
#[deriving(Eq)]
pub enum ReflowGoal {
/// We're reflowing in order to send a display list to the screen.
ReflowForDisplay,
/// We're reflowing in order to satisfy a script query. No display list will be created.
ReflowForScriptQuery,
}
/// Information needed for a reflow.
pub struct BuildData {
node: AbstractNode<ScriptView>,
/// What reflow needs to be done.
damage: Damage,
pub struct Reflow {
/// The document node.
document_root: AbstractNode<ScriptView>,
/// The style changes that need to be done.
damage: DocumentDamage,
/// The goal of reflow: either to render to the screen or to flush layout info for script.
goal: ReflowGoal,
/// 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>,
/// The channel that we send a notification to.
script_join_chan: Chan<()>,
}

View file

@ -7,12 +7,13 @@
use dom::bindings::utils::GlobalStaticData;
use dom::document::Document;
use dom::event::{Event, ResizeEvent, ReflowEvent};
use dom::node::define_bindings;
use dom::event::{Event, ResizeEvent, ReflowEvent, ClickEvent};
use dom::node::{AbstractNode, ScriptView, define_bindings};
use dom::window::Window;
use layout_interface::{AddStylesheetMsg, BuildData, BuildMsg, Damage, LayoutQuery};
use layout_interface::{LayoutResponse, LayoutTask, MatchSelectorsDamage, NoDamage};
use layout_interface::{QueryMsg, ReflowDamage};
use layout_interface::{AddStylesheetMsg, DocumentDamage, DocumentDamageLevel, HitTestQuery};
use layout_interface::{HitTestResponse, LayoutQuery, LayoutResponse, LayoutTask};
use layout_interface::{MatchSelectorsDocumentDamage, QueryMsg, Reflow, ReflowDocumentDamage};
use layout_interface::{ReflowForDisplay, ReflowForScriptQuery, ReflowGoal, ReflowMsg};
use layout_interface;
use core::cast::transmute;
@ -132,8 +133,8 @@ pub struct ScriptContext {
/// The current size of the window, in pixels.
window_size: Size2D<uint>,
/// What parts of layout are dirty.
damage: Damage,
/// What parts of the document are dirty, if any.
damage: Option<DocumentDamage>,
}
fn global_script_context_key(_: @ScriptContext) {}
@ -199,7 +200,7 @@ impl ScriptContext {
root_frame: None,
window_size: Size2D(800, 600),
damage: MatchSelectorsDamage,
damage: None,
};
// Indirection for Rust Issue #6248, dynamic freeze scope artifically extended
let script_context_ptr = {
@ -283,7 +284,7 @@ impl ScriptContext {
null(),
&rval);
self.relayout()
self.reflow(ReflowForScriptQuery)
}
/// Handles a request to exit the script task and shut down layout.
@ -348,8 +349,11 @@ impl ScriptContext {
});
// Perform the initial reflow.
self.damage.add(MatchSelectorsDamage);
self.relayout();
self.damage = Some(DocumentDamage {
root: root_node,
level: MatchSelectorsDocumentDamage,
});
self.reflow(ReflowForDisplay);
// Define debug functions.
self.js_compartment.define_functions(debug_fns);
@ -383,19 +387,13 @@ impl ScriptContext {
}
}
/// Initiate an asynchronous relayout operation
pub fn trigger_relayout(&mut self, damage: Damage) {
self.damage.add(damage);
self.relayout();
}
/// This method will wait until the layout task has completed its current action, join the
/// layout task, and then request a new layout run. It won't wait for the new layout
/// computation to finish.
///
/// This function fails if there is no root frame.
fn relayout(&mut self) {
debug!("script: performing relayout");
fn reflow(&mut self, goal: ReflowGoal) {
debug!("script: performing reflow");
// Now, join the layout so that they will see the latest changes we have made.
self.join_layout();
@ -408,22 +406,36 @@ impl ScriptContext {
None => fail!(~"Tried to relayout with no root frame!"),
Some(ref root_frame) => {
// Send new document and relevant styles to layout.
let data = ~BuildData {
node: root_frame.document.root,
let reflow = ~Reflow {
document_root: root_frame.document.root,
url: copy root_frame.url,
goal: goal,
script_chan: self.script_chan.clone(),
window_size: self.window_size,
script_join_chan: join_chan,
damage: replace(&mut self.damage, NoDamage),
damage: replace(&mut self.damage, None).unwrap(),
};
self.layout_task.chan.send(BuildMsg(data))
self.layout_task.chan.send(ReflowMsg(reflow))
}
}
debug!("script: layout forked")
}
/// Reflows the entire document.
///
/// FIXME: This should basically never be used.
pub fn reflow_all(&mut self, goal: ReflowGoal) {
for self.root_frame.each |root_frame| {
ScriptContext::damage(&mut self.damage,
root_frame.document.root,
MatchSelectorsDocumentDamage)
}
self.reflow(goal)
}
/// Sends the given query to layout.
pub fn query_layout(&mut self, query: LayoutQuery) -> Result<LayoutResponse,()> {
self.join_layout();
@ -433,6 +445,26 @@ impl ScriptContext {
response_port.recv()
}
/// Adds the given damage.
fn damage(damage: &mut Option<DocumentDamage>,
root: AbstractNode<ScriptView>,
level: DocumentDamageLevel) {
match *damage {
None => {}
Some(ref mut damage) => {
// FIXME(pcwalton): This is wrong. We should trace up to the nearest ancestor.
damage.root = root;
damage.level.add(level);
return
}
}
*damage = Some(DocumentDamage {
root: root,
level: level,
})
}
/// This is the main entry point for receiving and dispatching DOM events.
///
/// TODO: Actually perform DOM event dispatch.
@ -441,25 +473,52 @@ impl ScriptContext {
ResizeEvent(new_width, new_height, response_chan) => {
debug!("script got resize event: %u, %u", new_width, new_height);
self.damage.add(ReflowDamage);
self.window_size = Size2D(new_width, new_height);
for self.root_frame.each |root_frame| {
ScriptContext::damage(&mut self.damage,
root_frame.document.root,
ReflowDocumentDamage);
}
if self.root_frame.is_some() {
self.relayout()
self.reflow(ReflowForDisplay)
}
response_chan.send(())
}
// FIXME(pcwalton): This reflows the entire document and is not incremental-y.
ReflowEvent => {
debug!("script got reflow event");
self.damage.add(MatchSelectorsDamage);
for self.root_frame.each |root_frame| {
ScriptContext::damage(&mut self.damage,
root_frame.document.root,
MatchSelectorsDocumentDamage);
}
if self.root_frame.is_some() {
self.relayout()
self.reflow(ReflowForDisplay)
}
}
ClickEvent(point) => {
debug!("ClickEvent: clicked at %?", point);
let root = match self.root_frame {
Some(ref frame) => frame.document.root,
None => fail!("root frame is None")
};
match self.query_layout(HitTestQuery(root, point)) {
Ok(node) => match node {
HitTestResponse(node) => debug!("clicked on %?", node.debug_str()),
_ => fail!(~"unexpected layout reply")
},
Err(()) => {
println(fmt!("layout query error"));
}
};
}
}
}
}

@ -1 +1 @@
Subproject commit 61c99b4b1aa8653b8ee8511b6c15099de441ac44
Subproject commit d916b9796668d4bb46a3b1b4976c95ab7da79837

@ -1 +1 @@
Subproject commit e3057f02d48bf43856a0c13ad17372647f3b934f
Subproject commit 57407e5209ec95e695e17447fa7c5dfea96629b3

@ -1 +1 @@
Subproject commit 61f46e5035685e8e3d8b554db3b1e90c21d63fa2
Subproject commit ebf1cc8f2e8a39f31da2575981fad966aa7da904

@ -1 +1 @@
Subproject commit eb35e3957834264e99f42274f9c5c442e747ed3b
Subproject commit 453bf81e021008f5eba29b135f07f4529e6c8b2e

@ -1 +1 @@
Subproject commit 36a651dd83089c01da888ee9b5fff14437d4bcb8
Subproject commit 3565b32ba3d15d31b02cc76bdf76d6b13fc88451

1
src/support/nss/nspr Submodule

@ -0,0 +1 @@
Subproject commit 8219c5c5137b94ae93cfb4afc1dc10a94384e1c6

1
src/support/nss/nss Submodule

@ -0,0 +1 @@
Subproject commit 8ba903a1af20ce461d4f4033ec1092a229bc7482

View file

@ -0,0 +1,10 @@
#ohhi {
background-color: red;
padding: 6px;
}
#mark {
background-color: blue;
padding: 12px;
}

View file

@ -0,0 +1,7 @@
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="test_slam_layout.css">
</head>
<body><div id="ohhi"></div></body><script src="test_slam_layout.js"></script>
</html>

View file

@ -0,0 +1,14 @@
var divs = document.getElementsByTagName("div");
var div = divs[0];
var count = 1000;
var start = new Date();
for (var i = 0; i < count; i++) {
if (i % 2 == 0)
div.setAttribute('id', 'ohhi');
else
div.setAttribute('id', 'mark');
div.getBoundingClientRect();
}
var stop = new Date();
window.alert((stop - start) / count * 1e6 + " ns/layout");