From c14a1372335aeaffdcadc363cb4dd0fefc5daa4d Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Tue, 28 May 2013 21:08:24 -0700 Subject: [PATCH 01/41] Update rust-geom --- src/support/geom/rust-geom | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/support/geom/rust-geom b/src/support/geom/rust-geom index 61f46e50356..ebf1cc8f2e8 160000 --- a/src/support/geom/rust-geom +++ b/src/support/geom/rust-geom @@ -1 +1 @@ -Subproject commit 61f46e5035685e8e3d8b554db3b1e90c21d63fa2 +Subproject commit ebf1cc8f2e8a39f31da2575981fad966aa7da904 From 233a2040daf94ed60177cc72fecd0627debce0af Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Tue, 28 May 2013 22:17:41 -0700 Subject: [PATCH 02/41] Implement the beginnings of the box model for render boxes --- src/components/gfx/geometry.rs | 13 ++- src/components/main/layout/box.rs | 89 ++++---------------- src/components/main/layout/model.rs | 125 ++++++++++++++++++++++++++++ src/components/main/servo.rc | 1 + 4 files changed, 155 insertions(+), 73 deletions(-) create mode 100644 src/components/main/layout/model.rs diff --git a/src/components/gfx/geometry.rs b/src/components/gfx/geometry.rs index d3b1f019df9..01cee75193a 100644 --- a/src/components/gfx/geometry.rs +++ b/src/components/gfx/geometry.rs @@ -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 } } diff --git a/src/components/main/layout/box.rs b/src/components/main/layout/box.rs index a9f834f4d9c..f5cac35f568 100644 --- a/src/components/main/layout/box.rs +++ b/src/components/main/layout/box.rs @@ -8,11 +8,13 @@ use css::node_style::StyledNode; use layout::context::LayoutContext; use layout::display_list_builder::{DisplayListBuilder, 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, + /// 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, } } @@ -367,6 +372,16 @@ 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. + do self.with_mut_base |base| { + // TODO(pcwalton): Hmm, it seems wasteful to have the box model stuff inside every + // render box if they can only be nonzero if the box is an element. + if base.node.is_element() { + base.model.populate(base.node.style()) + } + } + 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 @@ -489,13 +504,6 @@ 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 { @@ -684,70 +692,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, abs_bounds: &Rect) { - 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(); @@ -870,3 +814,4 @@ pub impl RenderBox { } } + diff --git a/src/components/main/layout/model.rs b/src/components/main/layout/model.rs new file mode 100644 index 00000000000..35b545baebf --- /dev/null +++ b/src/components/main/layout/model.rs @@ -0,0 +1,125 @@ +/* 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::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}; + +/// Encapsulates the borders, padding, and margins, which we collectively call the "box model". +pub struct BoxModel { + border: SideOffsets2D, + padding: SideOffsets2D, + margin: SideOffsets2D, +} + +impl Zero for BoxModel { + fn zero() -> BoxModel { + BoxModel { + border: Zero::zero(), + padding: Zero::zero(), + margin: 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 populate(&mut self, style: CompleteStyle) { + // Populate 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()); + + // TODO(pcwalton): Padding, margins. + } + + /// Helper function to compute the border width in app units from the CSS border width. + 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), + } + } +} + +// +// 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(&self, list: &Cell, abs_bounds: &Rect) { + // Fast path. + let border = do self.with_imm_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 - 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"); + } + } + +} + diff --git a/src/components/main/servo.rc b/src/components/main/servo.rc index d2c29354bdd..50a599a0634 100755 --- a/src/components/main/servo.rc +++ b/src/components/main/servo.rc @@ -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; } From a1d12897e8fa51db776403f7855b937597a26470 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Wed, 29 May 2013 11:21:12 -0700 Subject: [PATCH 03/41] Add NSPR and NSS submodules --- .gitmodules | 6 ++++++ src/support/nss/nspr | 1 + src/support/nss/nss | 1 + 3 files changed, 8 insertions(+) create mode 160000 src/support/nss/nspr create mode 160000 src/support/nss/nss diff --git a/.gitmodules b/.gitmodules index 38368bc25bf..c08e1f13a16 100644 --- a/.gitmodules +++ b/.gitmodules @@ -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 diff --git a/src/support/nss/nspr b/src/support/nss/nspr new file mode 160000 index 00000000000..27b456e7d5a --- /dev/null +++ b/src/support/nss/nspr @@ -0,0 +1 @@ +Subproject commit 27b456e7d5a0f75381ab34b961f6779097007af7 diff --git a/src/support/nss/nss b/src/support/nss/nss new file mode 160000 index 00000000000..3d07e85a597 --- /dev/null +++ b/src/support/nss/nss @@ -0,0 +1 @@ +Subproject commit 3d07e85a597ce9c5c1ec80f85983efcb26aa58e1 From 0af3bbf953a6765f5fdef32cec7a93fc74a2a338 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Wed, 29 May 2013 12:19:55 -0700 Subject: [PATCH 04/41] Add NSS and NSPR to the build --- configure | 2 ++ mk/sub.mk | 2 ++ src/support/nss/nspr | 2 +- src/support/nss/nss | 2 +- 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/configure b/configure index e6dddb7ea3a..05f223e53a8 100755 --- a/configure +++ b/configure @@ -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 \ diff --git a/mk/sub.mk b/mk/sub.mk index e8bf4d7c67a..80bcb003826 100644 --- a/mk/sub.mk +++ b/mk/sub.mk @@ -21,6 +21,8 @@ NATIVE_BUILDS += \ libwapcaplet \ mozjs \ skia \ + nss \ + nspr \ $(NULL) # NOTE: the make magic can only compute transitive build dependencies, diff --git a/src/support/nss/nspr b/src/support/nss/nspr index 27b456e7d5a..8219c5c5137 160000 --- a/src/support/nss/nspr +++ b/src/support/nss/nspr @@ -1 +1 @@ -Subproject commit 27b456e7d5a0f75381ab34b961f6779097007af7 +Subproject commit 8219c5c5137b94ae93cfb4afc1dc10a94384e1c6 diff --git a/src/support/nss/nss b/src/support/nss/nss index 3d07e85a597..725cf233061 160000 --- a/src/support/nss/nss +++ b/src/support/nss/nss @@ -1 +1 @@ -Subproject commit 3d07e85a597ce9c5c1ec80f85983efcb26aa58e1 +Subproject commit 725cf233061c9598fc7c27cdc1d6278742396a48 From 2d1a00c3cf02c9f91cc3e39a818421795d3a3b72 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Wed, 29 May 2013 14:29:25 -0700 Subject: [PATCH 05/41] Don't clip layers to the screen area --- src/components/main/layout/block.rs | 13 ++++++++----- src/components/main/layout/layout_task.rs | 14 ++++++++------ 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/components/main/layout/block.rs b/src/components/main/layout/block.rs index 1d0ec0f9d86..ac980be4bb6 100644 --- a/src/components/main/layout/block.rs +++ b/src/components/main/layout/block.rs @@ -10,12 +10,12 @@ use layout::display_list_builder::{DisplayListBuilder, FlowDisplayListBuilderMet use layout::flow::{BlockFlow, FlowContext, FlowData, InlineBlockFlow}; use layout::inline::InlineLayout; -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,8 +95,8 @@ 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); } } @@ -155,8 +155,11 @@ 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 + }; self.common.position.size.height = height; diff --git a/src/components/main/layout/layout_task.rs b/src/components/main/layout/layout_task.rs index fc5d2bacc25..a45d46e6df8 100644 --- a/src/components/main/layout/layout_task.rs +++ b/src/components/main/layout/layout_task.rs @@ -13,10 +13,6 @@ 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; @@ -42,7 +38,9 @@ use script::layout_interface::{MatchSelectorsDamage, Msg, NoDamage, QueryMsg, Re 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; pub fn create_layout_task(render_task: RenderTask, img_cache_task: ImageCacheTask, @@ -222,9 +220,13 @@ impl Layout { // TODO: Be smarter about what needs painting. layout_root.build_display_list(&builder, &layout_root.position(), display_list); + let root_size = do layout_root.with_base |base| { + base.position.size + }; + 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) + size: Size2D(root_size.width.to_px() as uint, root_size.height.to_px() as uint) }; self.render_task.channel.send(RenderMsg(render_layer)); From 67eb533fc6f15f22a9dda145d3a0203fabe7b503 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Wed, 29 May 2013 15:38:05 -0700 Subject: [PATCH 06/41] Clamp scrolling to the page boundaries --- src/components/main/compositing/mod.rs | 36 +++++++++++++++++++++----- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/src/components/main/compositing/mod.rs b/src/components/main/compositing/mod.rs index 1469acbb57c..c15bdfbe5f0 100644 --- a/src/components/main/compositing/mod.rs +++ b/src/components/main/compositing/mod.rs @@ -10,6 +10,7 @@ use windowing::{ApplicationMethods, WindowMethods}; 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; @@ -119,6 +120,8 @@ fn run_main_loop(port: Port, // 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 @@ -136,8 +139,11 @@ fn run_main_loop(port: Port, // 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, ~[]); + let mut (page_width, page_height) = (0.0f32, 0.0f32); + for buffers.each |buffer| { let width = buffer.rect.size.width as uint; let height = buffer.rect.size.height as uint; @@ -172,9 +178,14 @@ fn run_main_loop(port: Port, Some(_) => fail!(~"found unexpected layer kind"), }; + let origin = buffer.rect.origin; + let origin = Point2D(origin.x as f32, origin.y as f32); + + // Update the page width and height calculations. + page_width = page_width.max(&(origin.x + (width as f32))); + page_height = page_height.max(&(origin.y + (height 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); @@ -182,6 +193,8 @@ fn run_main_loop(port: Port, image_layer.common.set_transform(transform) } + *page_size = Size2D(page_width, page_height) + // TODO: Recycle the old buffers; send them back to the renderer to reuse if // it wishes. } @@ -205,7 +218,8 @@ fn run_main_loop(port: Port, // 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) } // When the user enters a new URL, load it. @@ -216,13 +230,21 @@ fn run_main_loop(port: Port, // 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() } From d97f002111bcebce1368f79d33e04a5870ed11e7 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Wed, 29 May 2013 16:59:07 -0700 Subject: [PATCH 07/41] Stop hammering on the compositor --- src/components/main/compositing/mod.rs | 4 +++- src/components/main/platform/common/glut_windowing.rs | 1 - 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/main/compositing/mod.rs b/src/components/main/compositing/mod.rs index c15bdfbe5f0..c06245bc8c2 100644 --- a/src/components/main/compositing/mod.rs +++ b/src/components/main/compositing/mod.rs @@ -193,10 +193,12 @@ fn run_main_loop(port: Port, image_layer.common.set_transform(transform) } - *page_size = Size2D(page_width, page_height) + *page_size = Size2D(page_width, page_height); // TODO: Recycle the old buffers; send them back to the renderer to reuse if // it wishes. + + window.set_needs_display() } } } diff --git a/src/components/main/platform/common/glut_windowing.rs b/src/components/main/platform/common/glut_windowing.rs index 1e33c66b277..8df2985425b 100644 --- a/src/components/main/platform/common/glut_windowing.rs +++ b/src/components/main/platform/common/glut_windowing.rs @@ -94,7 +94,6 @@ impl WindowMethods 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. From 02c57728c550cf43c0709f3da3a8481fd6fe8e8d Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Wed, 29 May 2013 17:58:58 -0700 Subject: [PATCH 08/41] Fix corrupted textures when resizing. --- src/components/gfx/compositor.rs | 3 ++- src/components/gfx/render_layers.rs | 6 +++--- src/components/gfx/render_task.rs | 2 +- src/components/main/compositing/mod.rs | 22 ++++++++++------------ 4 files changed, 16 insertions(+), 17 deletions(-) diff --git a/src/components/gfx/compositor.rs b/src/components/gfx/compositor.rs index 83f8bb597a0..742894f739a 100644 --- a/src/components/gfx/compositor.rs +++ b/src/components/gfx/compositor.rs @@ -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); } diff --git a/src/components/gfx/render_layers.rs b/src/components/gfx/render_layers.rs index 49ee76130a5..93c1bc59e80 100644 --- a/src/components/gfx/render_layers.rs +++ b/src/components/gfx/render_layers.rs @@ -50,8 +50,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 +61,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 +75,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; diff --git a/src/components/gfx/render_task.rs b/src/components/gfx/render_task.rs index 4b55382305c..2beabee6641 100644 --- a/src/components/gfx/render_task.rs +++ b/src/components/gfx/render_task.rs @@ -162,7 +162,7 @@ impl Renderer { }; debug!("renderer: returning surface"); - self.compositor.paint(layer_buffer_set); + self.compositor.paint(layer_buffer_set, render_layer.size); } } } diff --git a/src/components/main/compositing/mod.rs b/src/components/main/compositing/mod.rs index c06245bc8c2..63e4e55564b 100644 --- a/src/components/main/compositing/mod.rs +++ b/src/components/main/compositing/mod.rs @@ -60,8 +60,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), + /// Requests that the compositor shut down. + Exit, } /// Azure surface wrapping to work with the layers infrastructure. @@ -132,8 +134,11 @@ fn run_main_loop(port: Port, 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. @@ -142,7 +147,6 @@ fn run_main_loop(port: Port, // 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, ~[]); - let mut (page_width, page_height) = (0.0f32, 0.0f32); for buffers.each |buffer| { let width = buffer.rect.size.width as uint; @@ -181,10 +185,6 @@ fn run_main_loop(port: Port, let origin = buffer.rect.origin; let origin = Point2D(origin.x as f32, origin.y as f32); - // Update the page width and height calculations. - page_width = page_width.max(&(origin.x + (width as f32))); - page_height = page_height.max(&(origin.y + (height as f32))); - // Set the layer's transform. let transform = original_layer_transform.translate(origin.x, origin.y, @@ -193,8 +193,6 @@ fn run_main_loop(port: Port, image_layer.common.set_transform(transform) } - *page_size = Size2D(page_width, page_height); - // TODO: Recycle the old buffers; send them back to the renderer to reuse if // it wishes. @@ -265,8 +263,8 @@ fn run_main_loop(port: Port, /// 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) { + self.chan.send(Paint(layer_buffer_set, new_size)) } } From dcfabb71d129f5a8c70cad9a8ab0b607d2c160bb Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Wed, 29 May 2013 18:30:40 -0700 Subject: [PATCH 09/41] Don't try to remove whitespace twice if it's the only node. Fixes a failure on Wikipedia. --- src/components/main/layout/box_builder.rs | 43 ++++++++++++----------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/src/components/main/layout/box_builder.rs b/src/components/main/layout/box_builder.rs index 6a0404d2c3d..60953a7f86a 100644 --- a/src/components/main/layout/box_builder.rs +++ b/src/components/main/layout/box_builder.rs @@ -370,43 +370,46 @@ pub impl LayoutTreeBuilder { // of its RenderBox or FlowContext children, and possibly keep alive other junk let parent_flow = parent_ctx.default_collector.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); } } } + + 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); From e2bcd3648eee17fe7b6c650267f39470278d20ec Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Wed, 29 May 2013 19:01:07 -0700 Subject: [PATCH 10/41] Color links blue --- src/components/main/css/select.rs | 3 +++ src/components/main/css/select_handler.rs | 10 ++++++++++ src/support/css/rust-css | 2 +- 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/components/main/css/select.rs b/src/components/main/css/select.rs index 71f5327cedb..46c62de20ea 100644 --- a/src/components/main/css/select.rs +++ b/src/components/main/css/select.rs @@ -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 } " } diff --git a/src/components/main/css/select_handler.rs b/src/components/main/css/select_handler.rs index fb232460b90..80affef9d34 100644 --- a/src/components/main/css/select_handler.rs +++ b/src/components/main/css/select_handler.rs @@ -76,6 +76,16 @@ impl SelectHandler> for NodeSelectHandler { self.parent_node(node).is_none() } + fn node_is_link(&self, node: &AbstractNode) -> bool { + if node.is_element() { + do node.with_imm_element |element| { + "a" == element.tag_name + } + } else { + false + } + } + fn with_node_id(&self, node: &AbstractNode, f: &fn(Option<&str>) -> R) -> R { if !node.is_element() { fail!(~"attempting to style non-element node"); diff --git a/src/support/css/rust-css b/src/support/css/rust-css index e3057f02d48..8a642ca9c85 160000 --- a/src/support/css/rust-css +++ b/src/support/css/rust-css @@ -1 +1 @@ -Subproject commit e3057f02d48bf43856a0c13ad17372647f3b934f +Subproject commit 8a642ca9c853709bccc25f58960087d1f55d69e6 From 2e4cecc71827e799a4576835776535a2af8e87f3 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Thu, 30 May 2013 16:10:35 -0700 Subject: [PATCH 11/41] Add flows if requested to the display list info. This will be used for hit testing. --- src/components/gfx/display_list.rs | 43 ++++++------ src/components/gfx/render_layers.rs | 6 +- src/components/main/layout/block.rs | 14 ++-- src/components/main/layout/box.rs | 23 ++++--- .../main/layout/display_list_builder.rs | 67 +++++++++++++------ src/components/main/layout/flow.rs | 12 ++-- src/components/main/layout/inline.rs | 12 ++-- src/components/main/layout/model.rs | 7 +- 8 files changed, 112 insertions(+), 72 deletions(-) diff --git a/src/components/gfx/display_list.rs b/src/components/gfx/display_list.rs index 6ceeef76a9c..1583b1fcf6d 100644 --- a/src/components/gfx/display_list.rs +++ b/src/components/gfx/display_list.rs @@ -26,20 +26,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 { + priv list: ~[DisplayItem] } -impl DisplayList { +impl DisplayList { /// Creates a new display list. - pub fn new() -> DisplayList { + pub fn new() -> DisplayList { 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) { // FIXME(Issue #150): crashes //debug!("Adding display item %u: %?", self.len(), item); self.list.push(item) @@ -58,51 +58,54 @@ impl DisplayList { } /// One drawing command in the list. -pub enum DisplayItem { - SolidColorDisplayItemClass(~SolidColorDisplayItem), - TextDisplayItemClass(~TextDisplayItem), - ImageDisplayItemClass(~ImageDisplayItem), - BorderDisplayItemClass(~BorderDisplayItem), +pub enum DisplayItem { + SolidColorDisplayItemClass(~SolidColorDisplayItem), + TextDisplayItemClass(~TextDisplayItem), + ImageDisplayItemClass(~ImageDisplayItem), + BorderDisplayItemClass(~BorderDisplayItem), } /// Information common to all display items. -pub struct BaseDisplayItem { +pub struct BaseDisplayItem { /// The boundaries of the display item. /// /// TODO: Which coordinate system should this use? bounds: Rect, + + /// 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 { + base: BaseDisplayItem, color: Color, } /// Renders text. -pub struct TextDisplayItem { - base: BaseDisplayItem, +pub struct TextDisplayItem { + base: BaseDisplayItem, text_run: ~SendableTextRun, range: Range, color: Color, } /// Renders an image. -pub struct ImageDisplayItem { - base: BaseDisplayItem, +pub struct ImageDisplayItem { + base: BaseDisplayItem, image: ARC<~Image>, } /// Renders a border. -pub struct BorderDisplayItem { - base: BaseDisplayItem, +pub struct BorderDisplayItem { + base: BaseDisplayItem, /// The width of the border. width: Au, /// The color of the border. color: Color, } -impl DisplayItem { +impl DisplayItem { /// Renders this display item into the given render context. fn draw_into_context(&self, render_context: &RenderContext) { match *self { diff --git a/src/components/gfx/render_layers.rs b/src/components/gfx/render_layers.rs index 93c1bc59e80..a2cab09e2c1 100644 --- a/src/components/gfx/render_layers.rs +++ b/src/components/gfx/render_layers.rs @@ -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, size: Size2D } diff --git a/src/components/main/layout/block.rs b/src/components/main/layout/block.rs index ac980be4bb6..95872a3273c 100644 --- a/src/components/main/layout/block.rs +++ b/src/components/main/layout/block.rs @@ -6,7 +6,8 @@ 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; @@ -175,11 +176,11 @@ impl BlockFlowData { }); } - pub fn build_display_list_block(@mut self, - builder: &DisplayListBuilder, - dirty: &Rect, - offset: &Point2D, - list: &Cell) { + pub fn build_display_list_block(@mut self, + builder: &DisplayListBuilder, + dirty: &Rect, + offset: &Point2D, + list: &Cell>) { // add box that starts block context self.box.map(|&box| { box.build_display_list(builder, dirty, offset, list) @@ -195,3 +196,4 @@ impl BlockFlowData { } } } + diff --git a/src/components/main/layout/box.rs b/src/components/main/layout/box.rs index f5cac35f568..53b8826657b 100644 --- a/src/components/main/layout/box.rs +++ b/src/components/main/layout/box.rs @@ -6,7 +6,7 @@ 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; @@ -550,11 +550,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, - offset: &Point2D, - list: &Cell) { + fn build_display_list(&self, + _: &DisplayListBuilder, + dirty: &Rect, + offset: &Point2D, + list: &Cell>) { let box_bounds = self.position(); let absolute_box_bounds = box_bounds.translate(offset); debug!("RenderBox::build_display_list at rel=%?, abs=%?: %s", @@ -582,6 +582,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(), @@ -602,6 +603,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(), @@ -621,6 +623,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(), @@ -644,6 +647,7 @@ pub impl RenderBox { let image_display_item = ~ImageDisplayItem { base: BaseDisplayItem { bounds: absolute_box_bounds, + extra: ExtraDisplayListData::new(*self), }, image: image.clone(), }; @@ -668,9 +672,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, - absolute_bounds: &Rect) { + fn paint_background_if_applicable(&self, + list: &Cell>, + absolute_bounds: &Rect) { // 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 @@ -683,6 +687,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(), }; diff --git a/src/components/main/layout/display_list_builder.rs b/src/components/main/layout/display_list_builder.rs index 1d0ea27b7e5..02173ef7517 100644 --- a/src/components/main/layout/display_list_builder.rs +++ b/src/components/main/layout/display_list_builder.rs @@ -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, c: &Cell); - fn build_display_list_for_child(&self, - a: &DisplayListBuilder, - b: FlowContext, - c: &Rect, - d: &Point2D, - e: &Cell); + fn build_display_list(&self, + a: &DisplayListBuilder, + b: &Rect, + c: &Cell>); + fn build_display_list_for_child(&self, + a: &DisplayListBuilder, + b: FlowContext, + c: &Rect, + d: &Point2D, + e: &Cell>); } impl FlowDisplayListBuilderMethods for FlowContext { - fn build_display_list(&self, - builder: &DisplayListBuilder, - dirty: &Rect, - list: &Cell) { + fn build_display_list(&self, + builder: &DisplayListBuilder, + dirty: &Rect, + list: &Cell>) { 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, - offset: &Point2D, - list: &Cell) { + fn build_display_list_for_child(&self, + builder: &DisplayListBuilder, + child_flow: FlowContext, + dirty: &Rect, + offset: &Point2D, + list: &Cell>) { // 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); diff --git a/src/components/main/layout/flow.rs b/src/components/main/layout/flow.rs index c840a204d7f..4a9a6f9dd62 100644 --- a/src/components/main/layout/flow.rs +++ b/src/components/main/layout/flow.rs @@ -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, - offset: &Point2D, - list: &Cell) { + pub fn build_display_list_recurse(&self, + builder: &DisplayListBuilder, + dirty: &Rect, + offset: &Point2D, + list: &Cell>) { do self.with_base |info| { debug!("FlowContext::build_display_list at %?: %s", info.position, self.debug_str()); } diff --git a/src/components/main/layout/inline.rs b/src/components/main/layout/inline.rs index 3db7e23e34e..704c62c20c4 100644 --- a/src/components/main/layout/inline.rs +++ b/src/components/main/layout/inline.rs @@ -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}; @@ -864,11 +864,11 @@ impl InlineFlowData { self.common.position.size.height = cur_y; } - pub fn build_display_list_inline(&self, - builder: &DisplayListBuilder, - dirty: &Rect, - offset: &Point2D, - list: &Cell) { + pub fn build_display_list_inline(&self, + builder: &DisplayListBuilder, + dirty: &Rect, + offset: &Point2D, + list: &Cell>) { // 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", diff --git a/src/components/main/layout/model.rs b/src/components/main/layout/model.rs index 35b545baebf..da92788a338 100644 --- a/src/components/main/layout/model.rs +++ b/src/components/main/layout/model.rs @@ -4,7 +4,7 @@ //! Borders, padding, and margins. -use layout::display_list_builder::ToGfxColor; +use layout::display_list_builder::{ExtraDisplayListData, ToGfxColor}; use layout::box::RenderBox; use core::cell::Cell; @@ -76,7 +76,9 @@ impl BoxModel { 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(&self, list: &Cell, abs_bounds: &Rect) { + pub fn paint_borders_if_applicable(&self, + list: &Cell>, + abs_bounds: &Rect) { // Fast path. let border = do self.with_imm_base |base| { base.model.border @@ -109,6 +111,7 @@ impl RenderBox { let border_display_item = ~BorderDisplayItem { base: BaseDisplayItem { bounds: bounds, + extra: ExtraDisplayListData::new(*self), }, width: border_width, color: color, From ea1a406589b391fcf0dbcf523969e134decaf227 Mon Sep 17 00:00:00 2001 From: Tim Kuehn Date: Fri, 24 May 2013 18:36:41 -0700 Subject: [PATCH 12/41] base and bounds methods for DisplayItem --- src/components/gfx/display_list.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/components/gfx/display_list.rs b/src/components/gfx/display_list.rs index 1583b1fcf6d..74c3bc0b45a 100644 --- a/src/components/gfx/display_list.rs +++ b/src/components/gfx/display_list.rs @@ -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; @@ -151,5 +152,21 @@ impl DisplayItem { } } } + + fn base<'a>(&'a self) -> &'a BaseDisplayItem { + // 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 { + self.base().bounds + } } From f77eef59883dcb96f99c5633b1cca7a8df545159 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Thu, 30 May 2013 17:28:08 -0700 Subject: [PATCH 13/41] Basic hit testing functionality --- src/components/gfx/display_list.rs | 2 +- src/components/main/compositing/mod.rs | 17 +++- src/components/main/layout/layout_task.rs | 88 ++++++++++++++++--- .../main/platform/common/glut_windowing.rs | 20 ++++- src/components/main/windowing.rs | 6 ++ src/components/script/dom/event.rs | 5 +- src/components/script/layout_interface.rs | 7 +- src/components/script/script_task.rs | 23 ++++- 8 files changed, 148 insertions(+), 20 deletions(-) diff --git a/src/components/gfx/display_list.rs b/src/components/gfx/display_list.rs index 74c3bc0b45a..8a7d5135242 100644 --- a/src/components/gfx/display_list.rs +++ b/src/components/gfx/display_list.rs @@ -28,7 +28,7 @@ use std::arc; /// A list of rendering operations to be performed. pub struct DisplayList { - priv list: ~[DisplayItem] + list: ~[DisplayItem] } impl DisplayList { diff --git a/src/components/main/compositing/mod.rs b/src/components/main/compositing/mod.rs index 63e4e55564b..c0b08be2f15 100644 --- a/src/components/main/compositing/mod.rs +++ b/src/components/main/compositing/mod.rs @@ -4,8 +4,9 @@ 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; @@ -222,10 +223,22 @@ fn run_main_loop(port: Port, 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. diff --git a/src/components/main/layout/layout_task.rs b/src/components/main/layout/layout_task.rs index a45d46e6df8..7034593fbe2 100644 --- a/src/components/main/layout/layout_task.rs +++ b/src/components/main/layout/layout_task.rs @@ -8,6 +8,7 @@ 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}; @@ -32,15 +33,17 @@ 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::{HitTestQuery, ContentBoxResponse, HitTestResponse}; +use script::layout_interface::{ContentBoxesQuery, ContentBoxesResponse, ExitMsg, LayoutQuery}; +use script::layout_interface::{LayoutResponse, LayoutTask, MatchSelectorsDamage, Msg, NoDamage}; +use script::layout_interface::{QueryMsg, ReflowDamage}; 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::{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, @@ -67,6 +70,8 @@ struct Layout { local_image_cache: @mut LocalImageCache, from_script: Port, font_ctx: @mut FontContext, + doc_url: Option, + screen_size: Option>, /// This is used to root reader data. layout_refs: ~[@mut LayoutData], @@ -90,6 +95,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, @@ -102,6 +110,21 @@ 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(); + let doc_url = self.doc_url.clone(); + + LayoutContext { + image_cache: image_cache, + font_ctx: font_ctx, + doc_url: doc_url.unwrap(), + 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), @@ -147,20 +170,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. // @@ -290,6 +309,55 @@ impl Layout { } }; + reply_chan.send(response) + } + HitTestQuery(node, point) => { + // FIXME: Isolate this transmutation into a single "bridge" module. + let node: AbstractNode = unsafe { + transmute(node) + }; + let mut flow_node: AbstractNode = 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> = + @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) } } diff --git a/src/components/main/platform/common/glut_windowing.rs b/src/components/main/platform/common/glut_windowing.rs index 8df2985425b..613064f2110 100644 --- a/src/components/main/platform/common/glut_windowing.rs +++ b/src/components/main/platform/common/glut_windowing.rs @@ -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; @@ -35,6 +35,7 @@ pub struct Window { composite_callback: Option, resize_callback: Option, load_url_callback: Option, + click_callback: Option, scroll_callback: Option, drag_origin: Point2D, @@ -54,6 +55,7 @@ impl WindowMethods for Window { composite_callback: None, resize_callback: None, load_url_callback: None, + click_callback: None, scroll_callback: None, drag_origin: Point2D(0, 0), @@ -77,6 +79,7 @@ impl WindowMethods for Window { window.handle_key(key) } do glut::mouse_func |_, _, x, y| { + window.handle_click(x, y); window.start_drag(x, y) } do glut::motion_func |x, y| { @@ -111,6 +114,11 @@ impl WindowMethods 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) @@ -136,6 +144,14 @@ impl Window { } } + /// 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 start a drag. fn start_drag(&mut self, x: c_int, y: c_int) { self.drag_origin = Point2D(x, y) diff --git a/src/components/main/windowing.rs b/src/components/main/windowing.rs index 708ea4105ec..9a9cefaf03b 100644 --- a/src/components/main/windowing.rs +++ b/src/components/main/windowing.rs @@ -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); + /// Type of the function that is called when the user scrolls. pub type ScrollCallback = @fn(Point2D); @@ -38,6 +42,8 @@ pub trait WindowMethods { 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); diff --git a/src/components/script/dom/event.rs b/src/components/script/dom/event.rs index 7b1fba2ac37..a0ef39d8fca 100644 --- a/src/components/script/dom/event.rs +++ b/src/components/script/dom/event.rs @@ -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), } pub struct Event_ { diff --git a/src/components/script/layout_interface.rs b/src/components/script/layout_interface.rs index b442dc440d3..86077244ecd 100644 --- a/src/components/script/layout_interface.rs +++ b/src/components/script/layout_interface.rs @@ -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; @@ -43,6 +44,8 @@ pub enum LayoutQuery { ContentBoxQuery(AbstractNode), /// Requests the dimensions of all the content boxes, as in the `getClientRects()` call. ContentBoxesQuery(AbstractNode), + /// Requests the node containing the point of interest + HitTestQuery(AbstractNode, Point2D), } /// The reply of a synchronous message from script to layout. @@ -54,6 +57,8 @@ pub enum LayoutResponse { ContentBoxResponse(Rect), /// A response to the `ContentBoxesQuery` message. ContentBoxesResponse(~[Rect]), + /// A response to the `HitTestQuery` message. + HitTestResponse(AbstractNode), } /// Dirty bits for layout. diff --git a/src/components/script/script_task.rs b/src/components/script/script_task.rs index cf99428c677..094336bf1ab 100644 --- a/src/components/script/script_task.rs +++ b/src/components/script/script_task.rs @@ -7,11 +7,11 @@ use dom::bindings::utils::GlobalStaticData; use dom::document::Document; -use dom::event::{Event, ResizeEvent, ReflowEvent}; +use dom::event::{Event, ResizeEvent, ReflowEvent, ClickEvent}; use dom::node::define_bindings; use dom::window::Window; -use layout_interface::{AddStylesheetMsg, BuildData, BuildMsg, Damage, LayoutQuery}; -use layout_interface::{LayoutResponse, LayoutTask, MatchSelectorsDamage, NoDamage}; +use layout_interface::{AddStylesheetMsg, BuildData, BuildMsg, Damage, LayoutQuery, HitTestQuery}; +use layout_interface::{LayoutResponse, HitTestResponse, LayoutTask, MatchSelectorsDamage, NoDamage}; use layout_interface::{QueryMsg, ReflowDamage}; use layout_interface; @@ -460,6 +460,23 @@ impl ScriptContext { self.relayout() } } + + 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")); + } + }; + } } } } From facb70756c7cdb04207a0a0193af0bd5f20fefc1 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Thu, 30 May 2013 17:38:00 -0700 Subject: [PATCH 14/41] Update rust-css and rust-netsurfcss --- src/support/css/rust-css | 2 +- src/support/netsurfcss/rust-netsurfcss | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/support/css/rust-css b/src/support/css/rust-css index 8a642ca9c85..7a59bc7f27a 160000 --- a/src/support/css/rust-css +++ b/src/support/css/rust-css @@ -1 +1 @@ -Subproject commit 8a642ca9c853709bccc25f58960087d1f55d69e6 +Subproject commit 7a59bc7f27a2135668b9ca0f681f31a17dd68879 diff --git a/src/support/netsurfcss/rust-netsurfcss b/src/support/netsurfcss/rust-netsurfcss index 36a651dd830..3565b32ba3d 160000 --- a/src/support/netsurfcss/rust-netsurfcss +++ b/src/support/netsurfcss/rust-netsurfcss @@ -1 +1 @@ -Subproject commit 36a651dd83089c01da888ee9b5fff14437d4bcb8 +Subproject commit 3565b32ba3d15d31b02cc76bdf76d6b13fc88451 From d5e47933abd2a9df10dd7ea0c2010124f0948f0b Mon Sep 17 00:00:00 2001 From: Eric Atkinson Date: Thu, 30 May 2013 10:55:42 -0700 Subject: [PATCH 15/41] Add horizontal borders, margins and padding. Broken until rust-css supports padding --- src/components/main/layout/block.rs | 72 +++++++++++++++++++++++--- src/components/main/layout/box.rs | 80 ++++++++++++++++------------- src/components/main/layout/model.rs | 71 +++++++++++++++++++++++-- 3 files changed, 174 insertions(+), 49 deletions(-) diff --git a/src/components/main/layout/block.rs b/src/components/main/layout/block.rs index 95872a3273c..3e26acf669b 100644 --- a/src/components/main/layout/block.rs +++ b/src/components/main/layout/block.rs @@ -10,6 +10,7 @@ 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 core::cell::Cell; use geom::point::Point2D; @@ -104,6 +105,8 @@ impl BlockFlowData { /* 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 + box.compute_borders(); min_width = min_width.add(&box.get_min_width(ctx)); pref_width = pref_width.add(&box.get_pref_width(ctx)); }); @@ -112,6 +115,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. /// @@ -123,24 +177,28 @@ 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; + box.compute_padding(remaining_width); + let available_width = remaining_width - box.get_noncontent_width(); - let (left_used, right_used) = box.get_used_width(); - remaining_width -= left_used.add(&right_used); + do box.compute_width(remaining_width) |width, left_margin, right_margin| { + self.compute_horiz(width, left_margin, right_margin, available_width) } + + let content_box = box.content_box(); + x_offset = content_box.origin.x; + remaining_width = content_box.size.width; }); 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; } } diff --git a/src/components/main/layout/box.rs b/src/components/main/layout/box.rs index 53b8826657b..6033a924bfc 100644 --- a/src/components/main/layout/box.rs +++ b/src/components/main/layout/box.rs @@ -8,7 +8,7 @@ use css::node_style::StyledNode; use layout::context::LayoutContext; use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData, ToGfxColor}; use layout::flow::FlowContext; -use layout::model::BoxModel; +use layout::model::{BoxModel,MaybeAuto}; use layout::text; use core::cell::Cell; @@ -374,14 +374,6 @@ pub impl RenderBox { 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. - do self.with_mut_base |base| { - // TODO(pcwalton): Hmm, it seems wasteful to have the box model stuff inside every - // render box if they can only be nonzero if the box is an element. - if base.node.is_element() { - base.model.populate(base.node.style()) - } - } - 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 @@ -456,37 +448,51 @@ pub impl RenderBox { (Au(0), Au(0)) } + fn compute_padding(&self, cb_width: Au) { + do self.with_imm_base |base| { + base.model.compute_padding(self.style(), cb_width); + } + } + + fn compute_borders(&self){ + do self.with_imm_base |base| { + base.model.compute_borders(self.style()); + } + } + + fn get_noncontent_width(&self) -> Au { + do self.with_imm_base |base| { + base.model.border.left + base.model.padding.left + base.model.border.right + + base.model.padding.right + } + } + + fn compute_width (&self, cb_width: Au, + callback: &fn(MaybeAuto, MaybeAuto, MaybeAuto) -> (Au, Au, Au)) { + let computed_width = MaybeAuto::from_width(self.style().width()); + let computed_margin_left = MaybeAuto::from_margin(self.style().margin_left()); + let computed_margin_right = MaybeAuto::from_margin(self.style().margin_right()); + + let (used_width, used_margin_left, used_margin_right) = + callback(computed_width, computed_margin_left, computed_margin_right); + + do self.with_mut_base |base| { + base.model.margin.left = used_margin_left; + base.model.margin.right = used_margin_right; + base.position.size.width = used_width + self.get_noncontent_width(); + base.position.origin.x = used_margin_left; + } + } + /// 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 { - 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_imm_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) } } diff --git a/src/components/main/layout/model.rs b/src/components/main/layout/model.rs index da92788a338..d0fb4ec083d 100644 --- a/src/components/main/layout/model.rs +++ b/src/components/main/layout/model.rs @@ -19,12 +19,54 @@ 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, padding: SideOffsets2D, margin: SideOffsets2D, + content_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 { @@ -33,24 +75,31 @@ impl Zero for BoxModel { border: Zero::zero(), padding: Zero::zero(), margin: Zero::zero(), + content_width: Zero::zero(), } } fn is_zero(&self) -> bool { - self.padding.is_zero() && self.border.is_zero() && self.margin.is_zero() + self.padding.is_zero() && self.border.is_zero() && self.margin.is_zero() && + self.content_width.is_zero() } } impl BoxModel { /// Populates the box model parameters from the given computed style. - pub fn populate(&mut self, style: CompleteStyle) { - // Populate the borders. + 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()); + } - // TODO(pcwalton): Padding, margins. + pub fn compute_padding(&mut self, style: CompleteStyle, cb_width: Au){ + self.padding.top = self.compute_padding(style.padding_top(), cb_width); + self.padding.right = self.compute_padding(style.padding_right(), cb_width); + self.padding.bottom = self.compute_padding(style.padding_bottom(), cb_width); + self.padding.left = self.compute_padding(style.padding_left(), cb_width); } /// Helper function to compute the border width in app units from the CSS border width. @@ -67,6 +116,18 @@ impl BoxModel { CSSBorderWidthThick => Au::from_px(10), } } + + fn compute_padding(&self, padding: CSSPadding, cb_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) => cb_width.scale_by(p) + } + } } // From 708f9b4db5116fc472c3a575dbd2a0dc84f3beee Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Thu, 30 May 2013 18:14:50 -0700 Subject: [PATCH 16/41] Fix method names and dynamic borrow check failures --- src/components/main/layout/box.rs | 15 +++++++++------ src/components/main/layout/model.rs | 10 +++++----- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/components/main/layout/box.rs b/src/components/main/layout/box.rs index 6033a924bfc..4e79a6205ab 100644 --- a/src/components/main/layout/box.rs +++ b/src/components/main/layout/box.rs @@ -449,14 +449,14 @@ pub impl RenderBox { } fn compute_padding(&self, cb_width: Au) { - do self.with_imm_base |base| { - base.model.compute_padding(self.style(), cb_width); + do self.with_mut_base |base| { + base.model.compute_padding(base.node.style(), cb_width); } } fn compute_borders(&self){ - do self.with_imm_base |base| { - base.model.compute_borders(self.style()); + do self.with_mut_base |base| { + base.model.compute_borders(base.node.style()); } } @@ -467,7 +467,8 @@ pub impl RenderBox { } } - fn compute_width (&self, cb_width: Au, + fn compute_width(&self, + cb_width: Au, callback: &fn(MaybeAuto, MaybeAuto, MaybeAuto) -> (Au, Au, Au)) { let computed_width = MaybeAuto::from_width(self.style().width()); let computed_margin_left = MaybeAuto::from_margin(self.style().margin_left()); @@ -476,10 +477,12 @@ pub impl RenderBox { let (used_width, used_margin_left, used_margin_right) = callback(computed_width, computed_margin_left, computed_margin_right); + let noncontent_width = self.get_noncontent_width(); + do self.with_mut_base |base| { base.model.margin.left = used_margin_left; base.model.margin.right = used_margin_right; - base.position.size.width = used_width + self.get_noncontent_width(); + base.position.size.width = used_width + noncontent_width; base.position.origin.x = used_margin_left; } } diff --git a/src/components/main/layout/model.rs b/src/components/main/layout/model.rs index d0fb4ec083d..9ed6a5b8de4 100644 --- a/src/components/main/layout/model.rs +++ b/src/components/main/layout/model.rs @@ -96,10 +96,10 @@ impl BoxModel { } pub fn compute_padding(&mut self, style: CompleteStyle, cb_width: Au){ - self.padding.top = self.compute_padding(style.padding_top(), cb_width); - self.padding.right = self.compute_padding(style.padding_right(), cb_width); - self.padding.bottom = self.compute_padding(style.padding_bottom(), cb_width); - self.padding.left = self.compute_padding(style.padding_left(), cb_width); + 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); } /// Helper function to compute the border width in app units from the CSS border width. @@ -117,7 +117,7 @@ impl BoxModel { } } - fn compute_padding(&self, padding: CSSPadding, cb_width: Au) -> Au{ + fn compute_padding_length(&self, padding: CSSPadding, cb_width: Au) -> Au{ match padding { CSSPaddingLength(Px(v)) | CSSPaddingLength(Pt(v)) | From 0b91af36774553210843c74723a6fcf8a1593fd8 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Thu, 30 May 2013 22:56:15 -0700 Subject: [PATCH 17/41] Refactor a bit and compute vertical margins as well. --- src/components/main/layout/block.rs | 47 +++++++++++++++++++---------- src/components/main/layout/box.rs | 15 +++++---- src/components/main/layout/model.rs | 22 ++++++++++---- 3 files changed, 54 insertions(+), 30 deletions(-) diff --git a/src/components/main/layout/block.rs b/src/components/main/layout/block.rs index 3e26acf669b..0dee3e1c45f 100644 --- a/src/components/main/layout/block.rs +++ b/src/components/main/layout/block.rs @@ -106,7 +106,9 @@ impl BlockFlowData { 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 - box.compute_borders(); + do box.with_model |model| { + model.compute_borders(box.style()) + } min_width = min_width.add(&box.get_min_width(ctx)); pref_width = pref_width.add(&box.get_pref_width(ctx)); }); @@ -182,16 +184,37 @@ impl BlockFlowData { let mut x_offset = Au(0); self.box.map(|&box| { - box.compute_padding(remaining_width); - let available_width = remaining_width - box.get_noncontent_width(); + do box.with_model |model| { + model.compute_padding(box.style(), remaining_width); - do box.compute_width(remaining_width) |width, left_margin, right_margin| { - self.compute_horiz(width, left_margin, right_margin, available_width) + let available_width = remaining_width - model.noncontent_width(); + + let margin_top = MaybeAuto::from_margin(box.style().margin_top()).spec_or_default(Au(0)); + let margin_bottom = MaybeAuto::from_margin(box.style().margin_bottom()).spec_or_default(Au(0)); + + let (width, margin_left, margin_right) = (MaybeAuto::from_width(box.style().width()), + MaybeAuto::from_margin(box.style().margin_left()), + MaybeAuto::from_margin(box.style().margin_right())); + + let (width, 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(); } - let content_box = box.content_box(); - x_offset = content_box.origin.x; - remaining_width = content_box.size.width; + do box.with_mut_base |base| { + let bp_width = base.model.padding.left + base.model.padding.right + + base.model.border.left + base.model.border.right; + + base.position.size.width = remaining_width + bp_width; + base.position.origin.x = base.model.margin.left; + } }); for BlockFlow(self).each_child |kid| { @@ -222,15 +245,7 @@ impl BlockFlowData { 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(); - } }); } diff --git a/src/components/main/layout/box.rs b/src/components/main/layout/box.rs index 4e79a6205ab..2b825ba67a1 100644 --- a/src/components/main/layout/box.rs +++ b/src/components/main/layout/box.rs @@ -8,7 +8,7 @@ use css::node_style::StyledNode; use layout::context::LayoutContext; use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData, ToGfxColor}; use layout::flow::FlowContext; -use layout::model::{BoxModel,MaybeAuto}; +use layout::model::{BoxModel}; use layout::text; use core::cell::Cell; @@ -487,16 +487,15 @@ pub impl RenderBox { } } + fn with_model(&self, callback: &fn(&mut BoxModel) -> R) -> R { + do self.with_imm_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 { - do self.with_imm_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) - } } /// The box formed by the border edge as defined in CSS 2.1 § 8.1. Coordinates are relative to diff --git a/src/components/main/layout/model.rs b/src/components/main/layout/model.rs index 9ed6a5b8de4..765ac12e5b1 100644 --- a/src/components/main/layout/model.rs +++ b/src/components/main/layout/model.rs @@ -27,7 +27,7 @@ pub struct BoxModel { border: SideOffsets2D, padding: SideOffsets2D, margin: SideOffsets2D, - content_width: Au, + cb_width: Au, } /// Useful helper data type when computing values for blocks and positioned elements. @@ -75,13 +75,12 @@ impl Zero for BoxModel { border: Zero::zero(), padding: Zero::zero(), margin: Zero::zero(), - content_width: Zero::zero(), + cb_width: Zero::zero(), } } fn is_zero(&self) -> bool { - self.padding.is_zero() && self.border.is_zero() && self.margin.is_zero() && - self.content_width.is_zero() + self.padding.is_zero() && self.border.is_zero() && self.margin.is_zero() } } @@ -102,8 +101,18 @@ impl BoxModel { 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. - fn compute_border_width(&self, width: CSSBorderWidth) -> Au { + priv fn compute_border_width(&self, width: CSSBorderWidth) -> Au { match width { CSSBorderWidthLength(Px(v)) | CSSBorderWidthLength(Em(v)) | @@ -117,7 +126,7 @@ impl BoxModel { } } - fn compute_padding_length(&self, padding: CSSPadding, cb_width: Au) -> Au{ + priv fn compute_padding_length(&self, padding: CSSPadding, cb_width: Au) -> Au { match padding { CSSPaddingLength(Px(v)) | CSSPaddingLength(Pt(v)) | @@ -128,6 +137,7 @@ impl BoxModel { CSSPaddingPercentage(p) => cb_width.scale_by(p) } } + } // From 72ca765cd0ae6e49f0c7b277bee5843918a8a1dd Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Fri, 31 May 2013 17:22:01 -0700 Subject: [PATCH 18/41] Update rust-css and fix some dynamic borrow check failures --- src/components/main/layout/block.rs | 16 ++++++----- src/components/main/layout/box.rs | 43 +++++++++-------------------- src/components/main/layout/model.rs | 4 +-- src/support/css/rust-css | 2 +- 4 files changed, 25 insertions(+), 40 deletions(-) diff --git a/src/components/main/layout/block.rs b/src/components/main/layout/block.rs index 0dee3e1c45f..03e9c62dcf7 100644 --- a/src/components/main/layout/block.rs +++ b/src/components/main/layout/block.rs @@ -106,8 +106,9 @@ impl BlockFlowData { 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(box.style()) + model.compute_borders(style) } min_width = min_width.add(&box.get_min_width(ctx)); pref_width = pref_width.add(&box.get_pref_width(ctx)); @@ -184,17 +185,18 @@ impl BlockFlowData { let mut x_offset = Au(0); self.box.map(|&box| { + let style = box.style(); do box.with_model |model| { - model.compute_padding(box.style(), remaining_width); + model.compute_padding(style, remaining_width); let available_width = remaining_width - model.noncontent_width(); - let margin_top = MaybeAuto::from_margin(box.style().margin_top()).spec_or_default(Au(0)); - let margin_bottom = MaybeAuto::from_margin(box.style().margin_bottom()).spec_or_default(Au(0)); + let margin_top = MaybeAuto::from_margin(style.margin_top()).spec_or_default(Au(0)); + let margin_bottom = MaybeAuto::from_margin(style.margin_bottom()).spec_or_default(Au(0)); - let (width, margin_left, margin_right) = (MaybeAuto::from_width(box.style().width()), - MaybeAuto::from_margin(box.style().margin_left()), - MaybeAuto::from_margin(box.style().margin_right())); + let (width, margin_left, margin_right) = (MaybeAuto::from_width(style.width()), + MaybeAuto::from_margin(style.margin_left()), + MaybeAuto::from_margin(style.margin_right())); let (width, margin_left, margin_right) = self.compute_horiz(width, margin_left, margin_right, available_width); diff --git a/src/components/main/layout/box.rs b/src/components/main/layout/box.rs index 2b825ba67a1..3cfeaa9c10e 100644 --- a/src/components/main/layout/box.rs +++ b/src/components/main/layout/box.rs @@ -8,7 +8,7 @@ use css::node_style::StyledNode; use layout::context::LayoutContext; use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData, ToGfxColor}; use layout::flow::FlowContext; -use layout::model::{BoxModel}; +use layout::model::BoxModel; use layout::text; use core::cell::Cell; @@ -454,41 +454,15 @@ pub impl RenderBox { } } - fn compute_borders(&self){ - do self.with_mut_base |base| { - base.model.compute_borders(base.node.style()); - } - } - fn get_noncontent_width(&self) -> Au { do self.with_imm_base |base| { - base.model.border.left + base.model.padding.left + base.model.border.right + - base.model.padding.right - } - } - - fn compute_width(&self, - cb_width: Au, - callback: &fn(MaybeAuto, MaybeAuto, MaybeAuto) -> (Au, Au, Au)) { - let computed_width = MaybeAuto::from_width(self.style().width()); - let computed_margin_left = MaybeAuto::from_margin(self.style().margin_left()); - let computed_margin_right = MaybeAuto::from_margin(self.style().margin_right()); - - let (used_width, used_margin_left, used_margin_right) = - callback(computed_width, computed_margin_left, computed_margin_right); - - let noncontent_width = self.get_noncontent_width(); - - do self.with_mut_base |base| { - base.model.margin.left = used_margin_left; - base.model.margin.right = used_margin_right; - base.position.size.width = used_width + noncontent_width; - base.position.origin.x = used_margin_left; + base.model.border.left + base.model.padding.left + + base.model.border.right + base.model.padding.right } } fn with_model(&self, callback: &fn(&mut BoxModel) -> R) -> R { - do self.with_imm_base |base| { + do self.with_mut_base |base| { callback(&mut base.model) } } @@ -496,6 +470,15 @@ pub impl RenderBox { /// 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 { + do self.with_imm_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) + } } /// The box formed by the border edge as defined in CSS 2.1 § 8.1. Coordinates are relative to diff --git a/src/components/main/layout/model.rs b/src/components/main/layout/model.rs index 765ac12e5b1..cc38cffcc40 100644 --- a/src/components/main/layout/model.rs +++ b/src/components/main/layout/model.rs @@ -31,12 +31,12 @@ pub struct BoxModel { } /// Useful helper data type when computing values for blocks and positioned elements. -pub enum MaybeAuto{ +pub enum MaybeAuto { Auto, Specified(Au), } -impl MaybeAuto{ +impl MaybeAuto { pub fn from_margin(margin: CSSMargin) -> MaybeAuto{ match margin { CSSMarginAuto => Auto, diff --git a/src/support/css/rust-css b/src/support/css/rust-css index 7a59bc7f27a..2f3f03cbafb 160000 --- a/src/support/css/rust-css +++ b/src/support/css/rust-css @@ -1 +1 @@ -Subproject commit 7a59bc7f27a2135668b9ca0f681f31a17dd68879 +Subproject commit 2f3f03cbafb88608d4f89216da7172d3b754fbef From f1fcd4d32320667bb7b47df3a8af6be517a869be Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Fri, 31 May 2013 17:28:14 -0700 Subject: [PATCH 19/41] Add comments and compute heights properly --- src/components/main/layout/block.rs | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/src/components/main/layout/block.rs b/src/components/main/layout/block.rs index 03e9c62dcf7..48626c7f087 100644 --- a/src/components/main/layout/block.rs +++ b/src/components/main/layout/block.rs @@ -187,10 +187,13 @@ impl BlockFlowData { self.box.map(|&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); + //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()).spec_or_default(Au(0)); let margin_bottom = MaybeAuto::from_margin(style.margin_bottom()).spec_or_default(Au(0)); @@ -211,11 +214,12 @@ impl BlockFlowData { } do box.with_mut_base |base| { - let bp_width = base.model.padding.left + base.model.padding.right + - base.model.border.left + base.model.border.right; - - base.position.size.width = remaining_width + bp_width; + //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; } }); @@ -232,6 +236,12 @@ impl BlockFlowData { pub fn assign_height_block(@mut self, ctx: &LayoutContext) { let mut cur_y = Au(0); + self.box.map(|&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; @@ -245,9 +255,18 @@ impl BlockFlowData { cur_y }; + //TODO(eatkinson): compute heights using the 'height' property. self.common.position.size.height = height; self.box.map(|&box| { + do box.with_mut_base |base| { + //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; + } }); } From cddf67a0e62e060369cab3d96babbd2c0cfc4524 Mon Sep 17 00:00:00 2001 From: Eric Atkinson Date: Thu, 30 May 2013 18:08:41 -0700 Subject: [PATCH 20/41] Update border rendering --- src/components/main/layout/model.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/main/layout/model.rs b/src/components/main/layout/model.rs index cc38cffcc40..f7f012ee56c 100644 --- a/src/components/main/layout/model.rs +++ b/src/components/main/layout/model.rs @@ -165,12 +165,12 @@ impl RenderBox { let border_width = border.top; let bounds = Rect { origin: Point2D { - x: abs_bounds.origin.x - border_width / Au(2), - y: abs_bounds.origin.y - border_width / Au(2), + x: abs_bounds.origin.x, + y: abs_bounds.origin.y, }, size: Size2D { - width: abs_bounds.size.width + border_width, - height: abs_bounds.size.height + border_width + width: abs_bounds.size.width, + height: abs_bounds.size.height } }; From fb2ce2c00c30c875769f0857e18269cff1882862 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Mon, 3 Jun 2013 16:06:38 -0700 Subject: [PATCH 21/41] Rename `with_imm_base` to `with_base`. --- src/components/main/layout/box.rs | 14 +++++++------- src/components/main/layout/inline.rs | 8 ++++---- src/components/main/layout/model.rs | 2 +- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/components/main/layout/box.rs b/src/components/main/layout/box.rs index 3cfeaa9c10e..4e99a916b46 100644 --- a/src/components/main/layout/box.rs +++ b/src/components/main/layout/box.rs @@ -181,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(&self, callback: &fn(&RenderBoxBase) -> R) -> R { + fn with_base(&self, callback: &fn(&RenderBoxBase) -> R) -> R { match *self { GenericRenderBoxClass(generic_box) => callback(generic_box), ImageRenderBoxClass(image_box) => { @@ -215,7 +215,7 @@ pub impl RenderBox { /// A convenience function to return the position of this box. fn position(&self) -> Rect { - do self.with_imm_base |base| { + do self.with_base |base| { base.position } } @@ -455,7 +455,7 @@ pub impl RenderBox { } fn get_noncontent_width(&self) -> Au { - do self.with_imm_base |base| { + do self.with_base |base| { base.model.border.left + base.model.padding.left + base.model.border.right + base.model.padding.right } @@ -470,7 +470,7 @@ pub impl RenderBox { /// 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 { - do self.with_imm_base |base| { + do self.with_base |base| { let origin = Point2D(base.position.origin.x + base.model.border.left + base.model.padding.left, @@ -498,12 +498,12 @@ pub impl RenderBox { /// 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 { - 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 @@ -511,7 +511,7 @@ pub impl RenderBox { /// /// If there is no ancestor-or-self `Element` node, fails. fn nearest_ancestor_element(&self) -> AbstractNode { - do self.with_imm_base |base| { + do self.with_base |base| { let mut node = base.node; while !node.is_element() { match node.parent_node() { diff --git a/src/components/main/layout/inline.rs b/src/components/main/layout/inline.rs index 704c62c20c4..ffdad2f65e0 100644 --- a/src/components/main/layout/inline.rs +++ b/src/components/main/layout/inline.rs @@ -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)); } diff --git a/src/components/main/layout/model.rs b/src/components/main/layout/model.rs index f7f012ee56c..4871ddb5c85 100644 --- a/src/components/main/layout/model.rs +++ b/src/components/main/layout/model.rs @@ -151,7 +151,7 @@ impl RenderBox { list: &Cell>, abs_bounds: &Rect) { // Fast path. - let border = do self.with_imm_base |base| { + let border = do self.with_base |base| { base.model.border }; if border.is_zero() { From e6ff135c01e55ea0ca5a7aade3b0c0afb9b63290 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Mon, 3 Jun 2013 21:49:58 -0700 Subject: [PATCH 22/41] test_slam_layout, a new layout perf test case --- src/test/html/test_slam_layout.css | 10 ++++++++++ src/test/html/test_slam_layout.html | 7 +++++++ src/test/html/test_slam_layout.js | 14 ++++++++++++++ 3 files changed, 31 insertions(+) create mode 100644 src/test/html/test_slam_layout.css create mode 100644 src/test/html/test_slam_layout.html create mode 100644 src/test/html/test_slam_layout.js diff --git a/src/test/html/test_slam_layout.css b/src/test/html/test_slam_layout.css new file mode 100644 index 00000000000..fed7aa97a3a --- /dev/null +++ b/src/test/html/test_slam_layout.css @@ -0,0 +1,10 @@ +#ohhi { + background-color: red; + padding: 6px; +} + +#mark { + background-color: blue; + padding: 12px; +} + diff --git a/src/test/html/test_slam_layout.html b/src/test/html/test_slam_layout.html new file mode 100644 index 00000000000..55adc8109b0 --- /dev/null +++ b/src/test/html/test_slam_layout.html @@ -0,0 +1,7 @@ + + + + + +
+ diff --git a/src/test/html/test_slam_layout.js b/src/test/html/test_slam_layout.js new file mode 100644 index 00000000000..5d1726b904e --- /dev/null +++ b/src/test/html/test_slam_layout.js @@ -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"); From 8d3b6aefa8d6c558012cc633bf7002ca72db1326 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Tue, 4 Jun 2013 11:43:52 -0700 Subject: [PATCH 23/41] Stop rendering when script queries layout --- src/components/main/layout/layout_task.rs | 42 ++++++++++++----------- src/components/script/layout_interface.rs | 11 ++++++ src/components/script/script_task.rs | 17 ++++----- 3 files changed, 42 insertions(+), 28 deletions(-) diff --git a/src/components/main/layout/layout_task.rs b/src/components/main/layout/layout_task.rs index 7034593fbe2..69d1e6a031b 100644 --- a/src/components/main/layout/layout_task.rs +++ b/src/components/main/layout/layout_task.rs @@ -36,7 +36,7 @@ use script::layout_interface::{AddStylesheetMsg, BuildData, BuildMsg, ContentBox use script::layout_interface::{HitTestQuery, ContentBoxResponse, HitTestResponse}; use script::layout_interface::{ContentBoxesQuery, ContentBoxesResponse, ExitMsg, LayoutQuery}; use script::layout_interface::{LayoutResponse, LayoutTask, MatchSelectorsDamage, Msg, NoDamage}; -use script::layout_interface::{QueryMsg, ReflowDamage}; +use script::layout_interface::{QueryMsg, ReflowDamage, ReflowForDisplay}; use script::script_task::{ScriptMsg, SendEventMsg}; use servo_net::image_cache_task::{ImageCacheTask, ImageResponseMsg}; use servo_net::local_image_cache::LocalImageCache; @@ -227,29 +227,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 root_size = do layout_root.with_base |base| { - base.position.size - }; + // 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 render_layer = RenderLayer { - display_list: display_list.take(), - size: Size2D(root_size.width.to_px() as uint, root_size.height.to_px() as uint) - }; + let root_size = do layout_root.with_base |base| { + base.position.size + }; - self.render_task.channel.send(RenderMsg(render_layer)); - } // time(layout: display list building) + 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) + } // Tell script that we're done. data.script_join_chan.send(()); diff --git a/src/components/script/layout_interface.rs b/src/components/script/layout_interface.rs index 86077244ecd..b4570f2a972 100644 --- a/src/components/script/layout_interface.rs +++ b/src/components/script/layout_interface.rs @@ -86,11 +86,22 @@ impl Damage { } } +/// 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, /// What reflow needs to be done. damage: Damage, + /// 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. diff --git a/src/components/script/script_task.rs b/src/components/script/script_task.rs index 094336bf1ab..d82f4e0895e 100644 --- a/src/components/script/script_task.rs +++ b/src/components/script/script_task.rs @@ -12,7 +12,7 @@ use dom::node::define_bindings; use dom::window::Window; use layout_interface::{AddStylesheetMsg, BuildData, BuildMsg, Damage, LayoutQuery, HitTestQuery}; use layout_interface::{LayoutResponse, HitTestResponse, LayoutTask, MatchSelectorsDamage, NoDamage}; -use layout_interface::{QueryMsg, ReflowDamage}; +use layout_interface::{QueryMsg, ReflowDamage, ReflowForDisplay, ReflowForScriptQuery, ReflowGoal}; use layout_interface; use core::cast::transmute; @@ -283,7 +283,7 @@ impl ScriptContext { null(), &rval); - self.relayout() + self.relayout(ReflowForScriptQuery) } /// Handles a request to exit the script task and shut down layout. @@ -349,7 +349,7 @@ impl ScriptContext { // Perform the initial reflow. self.damage.add(MatchSelectorsDamage); - self.relayout(); + self.relayout(ReflowForDisplay); // Define debug functions. self.js_compartment.define_functions(debug_fns); @@ -383,10 +383,10 @@ impl ScriptContext { } } - /// Initiate an asynchronous relayout operation + /// Initiate an asynchronous relayout operation to handle a script layout query. pub fn trigger_relayout(&mut self, damage: Damage) { self.damage.add(damage); - self.relayout(); + self.relayout(ReflowForScriptQuery); } /// This method will wait until the layout task has completed its current action, join the @@ -394,7 +394,7 @@ impl ScriptContext { /// computation to finish. /// /// This function fails if there is no root frame. - fn relayout(&mut self) { + fn relayout(&mut self, goal: ReflowGoal) { debug!("script: performing relayout"); // Now, join the layout so that they will see the latest changes we have made. @@ -411,6 +411,7 @@ impl ScriptContext { let data = ~BuildData { node: 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, @@ -445,7 +446,7 @@ impl ScriptContext { self.window_size = Size2D(new_width, new_height); if self.root_frame.is_some() { - self.relayout() + self.relayout(ReflowForDisplay) } response_chan.send(()) @@ -457,7 +458,7 @@ impl ScriptContext { self.damage.add(MatchSelectorsDamage); if self.root_frame.is_some() { - self.relayout() + self.relayout(ReflowForDisplay) } } From 40a69fc51758356b9ecd9c5e3e3b2754cbb85fe7 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Tue, 4 Jun 2013 15:23:18 -0700 Subject: [PATCH 24/41] Address review comments --- src/components/main/layout/block.rs | 97 ++++++++++++++--------- src/components/main/layout/context.rs | 4 +- src/components/main/layout/layout_task.rs | 10 +-- src/components/main/layout/model.rs | 33 +++++--- 4 files changed, 85 insertions(+), 59 deletions(-) diff --git a/src/components/main/layout/block.rs b/src/components/main/layout/block.rs index 48626c7f087..07759121f4e 100644 --- a/src/components/main/layout/block.rs +++ b/src/components/main/layout/block.rs @@ -102,38 +102,39 @@ impl BlockFlowData { } } - /* 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 + // If not an anonymous block context, add in the block box's widths. These widths will not + // include child elements, just padding etc. + for self.box.each |&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)); - }); + } self.common.min_width = min_width; 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{ + /// Requires borders and padding to already be computed. + 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) { + if (left + right + width) > available_width { (Specified(left), Specified(right)) } else { (left_margin, right_margin) @@ -141,31 +142,43 @@ impl BlockFlowData { } }; - //Invariant: left_margin_Au + width_Au + right_margin_Au == available_width + // 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 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_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 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 + // 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 + // 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 + + // Return values in same order as params. (width_Au, left_margin_Au, right_margin_Au) } @@ -184,25 +197,31 @@ impl BlockFlowData { let mut remaining_width = self.common.position.size.width; let mut x_offset = Au(0); - self.box.map(|&box| { + for self.box.each |&box| { let style = box.style(); do box.with_model |model| { - //Can compute padding here since we know containing block width + // Can compute padding here since we know containing block width. model.compute_padding(style, remaining_width); - //Margins are 0 right now so model.noncontent_width() is just borders + padding. + // 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()).spec_or_default(Au(0)); - let margin_bottom = MaybeAuto::from_margin(style.margin_bottom()).spec_or_default(Au(0)); + // 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())); + let (width, margin_left, margin_right) = + (MaybeAuto::from_width(style.width()), + MaybeAuto::from_margin(style.margin_left()), + MaybeAuto::from_margin(style.margin_right())); - let (width, margin_left, margin_right) = - self.compute_horiz(width, margin_left, margin_right, available_width); + // 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; @@ -221,7 +240,7 @@ impl BlockFlowData { 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()); diff --git a/src/components/main/layout/context.rs b/src/components/main/layout/context.rs index 8646b9faabc..e0ad6bcad7e 100644 --- a/src/components/main/layout/context.rs +++ b/src/components/main/layout/context.rs @@ -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 + screen_size: Rect, } diff --git a/src/components/main/layout/layout_task.rs b/src/components/main/layout/layout_task.rs index 69d1e6a031b..659272fa62f 100644 --- a/src/components/main/layout/layout_task.rs +++ b/src/components/main/layout/layout_task.rs @@ -115,12 +115,10 @@ impl Layout { let image_cache = self.local_image_cache; let font_ctx = self.font_ctx; let screen_size = self.screen_size.unwrap(); - let doc_url = self.doc_url.clone(); LayoutContext { image_cache: image_cache, font_ctx: font_ctx, - doc_url: doc_url.unwrap(), screen_size: Rect(Point2D(Au(0), Au(0)), screen_size), } } @@ -348,10 +346,12 @@ impl Layout { let display_list = &display_list.take().list; for display_list.each_reverse |display_item| { let bounds = display_item.bounds(); + + // FIXME(pcwalton): Move this to be a method on Rect. if x <= bounds.origin.x + bounds.size.width && - bounds.origin.x <= x && - y < bounds.origin.y + bounds.size.height && - bounds.origin.y < y { + x >= bounds.origin.x && + y < bounds.origin.y + bounds.size.height && + y >= bounds.origin.y { resp = Ok(HitTestResponse(display_item.base().extra.node())); break; } diff --git a/src/components/main/layout/model.rs b/src/components/main/layout/model.rs index 4871ddb5c85..8f222814dc1 100644 --- a/src/components/main/layout/model.rs +++ b/src/components/main/layout/model.rs @@ -22,12 +22,17 @@ 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 { + /// The size of the borders. border: SideOffsets2D, + /// The size of the padding. padding: SideOffsets2D, + /// The size of the margins. margin: SideOffsets2D, - cb_width: Au, + /// The width of the content box. + content_box_width: Au, } /// Useful helper data type when computing values for blocks and positioned elements. @@ -75,7 +80,7 @@ impl Zero for BoxModel { border: Zero::zero(), padding: Zero::zero(), margin: Zero::zero(), - cb_width: Zero::zero(), + content_box_width: Zero::zero(), } } @@ -85,7 +90,7 @@ impl Zero for BoxModel { } impl BoxModel { - /// Populates the box model parameters from the given computed style. + /// Populates the box model border 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()); @@ -94,11 +99,16 @@ impl BoxModel { 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); + /// Populates the box model padding parameters from the given computed style. + pub fn compute_padding(&mut self, style: CompleteStyle, content_box_width: Au) { + self.padding.top = self.compute_padding_length(style.padding_top(), + content_box_width); + self.padding.right = self.compute_padding_length(style.padding_right(), + content_box_width); + self.padding.bottom = self.compute_padding_length(style.padding_bottom(), + content_box_width); + self.padding.left = self.compute_padding_length(style.padding_left(), + content_box_width); } pub fn noncontent_width(&self) -> Au { @@ -112,7 +122,7 @@ impl BoxModel { } /// Helper function to compute the border width in app units from the CSS border width. - priv fn compute_border_width(&self, width: CSSBorderWidth) -> Au { + fn compute_border_width(&self, width: CSSBorderWidth) -> Au { match width { CSSBorderWidthLength(Px(v)) | CSSBorderWidthLength(Em(v)) | @@ -126,7 +136,7 @@ impl BoxModel { } } - priv fn compute_padding_length(&self, padding: CSSPadding, cb_width: Au) -> Au { + fn compute_padding_length(&self, padding: CSSPadding, content_box_width: Au) -> Au { match padding { CSSPaddingLength(Px(v)) | CSSPaddingLength(Pt(v)) | @@ -134,10 +144,9 @@ impl BoxModel { // FIXME(eatkinson): Handle 'em' and 'pt' correctly Au::from_frac_px(v) } - CSSPaddingPercentage(p) => cb_width.scale_by(p) + CSSPaddingPercentage(p) => content_box_width.scale_by(p) } } - } // From 7a435fc6edf3c261c56f083b01b132aa6a37724f Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Tue, 4 Jun 2013 22:00:33 -0700 Subject: [PATCH 25/41] Refactor document damage to distinguish it from layout/style damage. Also, standardize on the name "reflow" instead of "relayout" or "build". --- src/components/main/layout/layout_task.rs | 21 ++--- src/components/script/dom/window.rs | 4 +- src/components/script/layout_interface.rs | 46 ++++++----- src/components/script/script_task.rs | 93 ++++++++++++++++------- 4 files changed, 106 insertions(+), 58 deletions(-) diff --git a/src/components/main/layout/layout_task.rs b/src/components/main/layout/layout_task.rs index 659272fa62f..19580507eeb 100644 --- a/src/components/main/layout/layout_task.rs +++ b/src/components/main/layout/layout_task.rs @@ -32,11 +32,12 @@ 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::{AddStylesheetMsg, ContentBoxQuery}; use script::layout_interface::{HitTestQuery, ContentBoxResponse, HitTestResponse}; use script::layout_interface::{ContentBoxesQuery, ContentBoxesResponse, ExitMsg, LayoutQuery}; -use script::layout_interface::{LayoutResponse, LayoutTask, MatchSelectorsDamage, Msg, NoDamage}; -use script::layout_interface::{QueryMsg, ReflowDamage, ReflowForDisplay}; +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; @@ -126,11 +127,11 @@ impl Layout { 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) => { @@ -154,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 = unsafe { - transmute(&data.node) + transmute(&data.document_root) }; // FIXME: Bad copy! @@ -187,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); } diff --git a/src/components/script/dom/window.rs b/src/components/script/dom/window.rs index c94e6488a67..a5904b642c7 100644 --- a/src/components/script/dom/window.rs +++ b/src/components/script/dom/window.rs @@ -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) } } diff --git a/src/components/script/layout_interface.rs b/src/components/script/layout_interface.rs index b4570f2a972..f9ab0a8eb2e 100644 --- a/src/components/script/layout_interface.rs +++ b/src/components/script/layout_interface.rs @@ -25,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. /// @@ -61,31 +59,37 @@ pub enum LayoutResponse { HitTestResponse(AbstractNode), } -/// 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, + /// The amount of damage that occurred. + level: DocumentDamageLevel, +} + /// Why we're doing reflow. #[deriving(Eq)] pub enum ReflowGoal { @@ -96,10 +100,11 @@ pub enum ReflowGoal { } /// Information needed for a reflow. -pub struct BuildData { - node: AbstractNode, - /// What reflow needs to be done. - damage: Damage, +pub struct Reflow { + /// The document node. + document_root: AbstractNode, + /// 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. @@ -108,6 +113,7 @@ pub struct BuildData { script_chan: SharedChan, /// The current window size. window_size: Size2D, + /// The channel that we send a notification to. script_join_chan: Chan<()>, } diff --git a/src/components/script/script_task.rs b/src/components/script/script_task.rs index d82f4e0895e..66a4e9e4727 100644 --- a/src/components/script/script_task.rs +++ b/src/components/script/script_task.rs @@ -8,11 +8,12 @@ use dom::bindings::utils::GlobalStaticData; use dom::document::Document; use dom::event::{Event, ResizeEvent, ReflowEvent, ClickEvent}; -use dom::node::define_bindings; +use dom::node::{AbstractNode, ScriptView, define_bindings}; use dom::window::Window; -use layout_interface::{AddStylesheetMsg, BuildData, BuildMsg, Damage, LayoutQuery, HitTestQuery}; -use layout_interface::{LayoutResponse, HitTestResponse, LayoutTask, MatchSelectorsDamage, NoDamage}; -use layout_interface::{QueryMsg, ReflowDamage, ReflowForDisplay, ReflowForScriptQuery, ReflowGoal}; +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, - /// What parts of layout are dirty. - damage: Damage, + /// What parts of the document are dirty, if any. + damage: Option, } 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(ReflowForScriptQuery) + 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(ReflowForDisplay); + 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 to handle a script layout query. - pub fn trigger_relayout(&mut self, damage: Damage) { - self.damage.add(damage); - self.relayout(ReflowForScriptQuery); - } - /// 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, goal: ReflowGoal) { - 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,23 +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 { self.join_layout(); @@ -434,6 +445,26 @@ impl ScriptContext { response_port.recv() } + /// Adds the given damage. + fn damage(damage: &mut Option, + root: AbstractNode, + 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. @@ -442,23 +473,33 @@ 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(ReflowForDisplay) + 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(ReflowForDisplay) + self.reflow(ReflowForDisplay) } } From 5750069e0aadd65a97cff9a8a76dca2219363aa3 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Wed, 5 Jun 2013 21:39:54 -0700 Subject: [PATCH 26/41] Use the scroll hack --- mk/sub.mk | 5 ++++ .../main/platform/common/glut_windowing.rs | 30 ++++++++----------- src/platform/macos/rust-cocoa | 2 +- src/support/glut/rust-glut | 2 +- 4 files changed, 19 insertions(+), 20 deletions(-) diff --git a/mk/sub.mk b/mk/sub.mk index 80bcb003826..b01f34329e8 100644 --- a/mk/sub.mk +++ b/mk/sub.mk @@ -42,6 +42,7 @@ DEPS_rust-glut += \ DEPS_rust-layers += \ rust-azure \ + rust-cocoa \ rust-geom \ rust-glut \ rust-opengles \ @@ -130,6 +131,10 @@ DEPS_rust-layers += \ rust-core-text \ $(NULL) +DEPS_rust-glut += \ + rust-cocoa \ + $(NULL) + endif ifeq ($(CFG_OSTYPE),unknown-linux-gnu) diff --git a/src/components/main/platform/common/glut_windowing.rs b/src/components/main/platform/common/glut_windowing.rs index 613064f2110..07a2f3bf9c2 100644 --- a/src/components/main/platform/common/glut_windowing.rs +++ b/src/components/main/platform/common/glut_windowing.rs @@ -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; @@ -78,14 +79,16 @@ impl WindowMethods for Window { do glut::keyboard_func |key, _, _| { window.handle_key(key) } - do glut::mouse_func |_, _, x, y| { - window.handle_click(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 } @@ -152,20 +155,11 @@ 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 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)), } } diff --git a/src/platform/macos/rust-cocoa b/src/platform/macos/rust-cocoa index 61c99b4b1aa..ff8e2a63938 160000 --- a/src/platform/macos/rust-cocoa +++ b/src/platform/macos/rust-cocoa @@ -1 +1 @@ -Subproject commit 61c99b4b1aa8653b8ee8511b6c15099de441ac44 +Subproject commit ff8e2a63938ded5d2688619680a981c73d25080d diff --git a/src/support/glut/rust-glut b/src/support/glut/rust-glut index eb35e395783..969af0260ef 160000 --- a/src/support/glut/rust-glut +++ b/src/support/glut/rust-glut @@ -1 +1 @@ -Subproject commit eb35e3957834264e99f42274f9c5c442e747ed3b +Subproject commit 969af0260ef38d2c80ef2f51037da7ed1fc5cc85 From ae5b2dfc727e6b1cc0938626b3eb8a6dc6657d34 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Wed, 5 Jun 2013 21:40:11 -0700 Subject: [PATCH 27/41] Roll up block layout changes --- src/components/main/layout/block.rs | 71 +++++++++-------------- src/components/main/layout/context.rs | 4 +- src/components/main/layout/layout_task.rs | 10 ++-- src/components/main/layout/model.rs | 23 +++----- 4 files changed, 44 insertions(+), 64 deletions(-) diff --git a/src/components/main/layout/block.rs b/src/components/main/layout/block.rs index 07759121f4e..6cdac91368c 100644 --- a/src/components/main/layout/block.rs +++ b/src/components/main/layout/block.rs @@ -102,39 +102,38 @@ impl BlockFlowData { } } - // If not an anonymous block context, add in the block box's widths. These widths will not - // include child elements, just padding etc. - for self.box.each |&box| { - // Can compute border width here since it doesn't depend on anything. + /* 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)); - } + }); self.common.min_width = min_width; 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. - 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 { + /// 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 { + if((left + right + width) > available_width) { (Specified(left), Specified(right)) } else { (left_margin, right_margin) @@ -142,43 +141,31 @@ impl BlockFlowData { } }; - // Invariant: left_margin_Au + width_Au + right_margin_Au == available_width + //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_l, width, available_width - (margin_l + width)) - } + //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 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. + //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. + //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. + //return values in same order as params (width_Au, left_margin_Au, right_margin_Au) } @@ -255,11 +242,11 @@ impl BlockFlowData { pub fn assign_height_block(@mut self, ctx: &LayoutContext) { let mut cur_y = Au(0); - self.box.map(|&box| { + 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| { diff --git a/src/components/main/layout/context.rs b/src/components/main/layout/context.rs index e0ad6bcad7e..8646b9faabc 100644 --- a/src/components/main/layout/context.rs +++ b/src/components/main/layout/context.rs @@ -8,10 +8,12 @@ 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, - screen_size: Rect, + doc_url: Url, + screen_size: Rect } diff --git a/src/components/main/layout/layout_task.rs b/src/components/main/layout/layout_task.rs index 19580507eeb..82a3a214b9c 100644 --- a/src/components/main/layout/layout_task.rs +++ b/src/components/main/layout/layout_task.rs @@ -116,10 +116,12 @@ impl Layout { let image_cache = self.local_image_cache; let font_ctx = self.font_ctx; let screen_size = self.screen_size.unwrap(); + let doc_url = self.doc_url.clone(); LayoutContext { image_cache: image_cache, font_ctx: font_ctx, + doc_url: doc_url.unwrap(), screen_size: Rect(Point2D(Au(0), Au(0)), screen_size), } } @@ -347,12 +349,10 @@ impl Layout { let display_list = &display_list.take().list; for display_list.each_reverse |display_item| { let bounds = display_item.bounds(); - - // FIXME(pcwalton): Move this to be a method on Rect. if x <= bounds.origin.x + bounds.size.width && - x >= bounds.origin.x && - y < bounds.origin.y + bounds.size.height && - y >= bounds.origin.y { + bounds.origin.x <= x && + y < bounds.origin.y + bounds.size.height && + bounds.origin.y < y { resp = Ok(HitTestResponse(display_item.base().extra.node())); break; } diff --git a/src/components/main/layout/model.rs b/src/components/main/layout/model.rs index 8f222814dc1..0260fd346c7 100644 --- a/src/components/main/layout/model.rs +++ b/src/components/main/layout/model.rs @@ -22,14 +22,10 @@ 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 { - /// The size of the borders. border: SideOffsets2D, - /// The size of the padding. padding: SideOffsets2D, - /// The size of the margins. margin: SideOffsets2D, /// The width of the content box. content_box_width: Au, @@ -90,7 +86,7 @@ impl Zero for BoxModel { } impl BoxModel { - /// Populates the box model border parameters from the given computed style. + /// 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()); @@ -99,16 +95,11 @@ impl BoxModel { self.border.left = self.compute_border_width(style.border_left_width()); } - /// Populates the box model padding parameters from the given computed style. - pub fn compute_padding(&mut self, style: CompleteStyle, content_box_width: Au) { - self.padding.top = self.compute_padding_length(style.padding_top(), - content_box_width); - self.padding.right = self.compute_padding_length(style.padding_right(), - content_box_width); - self.padding.bottom = self.compute_padding_length(style.padding_bottom(), - content_box_width); - self.padding.left = self.compute_padding_length(style.padding_left(), - content_box_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 { @@ -122,7 +113,7 @@ impl BoxModel { } /// Helper function to compute the border width in app units from the CSS border width. - fn compute_border_width(&self, width: CSSBorderWidth) -> Au { + priv fn compute_border_width(&self, width: CSSBorderWidth) -> Au { match width { CSSBorderWidthLength(Px(v)) | CSSBorderWidthLength(Em(v)) | From 9c25474ea6c0c61eae0762d616387a276dccce86 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Thu, 6 Jun 2013 10:47:30 -0700 Subject: [PATCH 28/41] Only warn, don't assert, if the node range length is zero. This broke http://www.all-about-ferrets.com/long-tailed-weasel.html --- src/components/main/layout/box_builder.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/main/layout/box_builder.rs b/src/components/main/layout/box_builder.rs index 60953a7f86a..5e895f4b4c9 100644 --- a/src/components/main/layout/box_builder.rs +++ b/src/components/main/layout/box_builder.rs @@ -177,7 +177,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); From 97eb5671bbf9e29559ac6ce5aa25f473ce65cadb Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Thu, 6 Jun 2013 13:47:52 -0700 Subject: [PATCH 29/41] Update the configure script to pass --enable-64bit --- configure | 11 +++++++---- src/support/nss/nss | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/configure b/configure index 05f223e53a8..5fffd078556 100755 --- a/configure +++ b/configure @@ -506,11 +506,14 @@ do if [ $i = "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 = "nspr" ]; then + CONFIGURE_ARGS="--enable-64bit" + fi + if [ $i = mozjs ]; then + if [ ! -z $CFG_ENABLE_DEBUG ]; then + CONFIGURE_ARGS="--enable-debug" fi + fi if [ -f ${CONFIGURE_SCRIPT} ] then diff --git a/src/support/nss/nss b/src/support/nss/nss index 725cf233061..9d997ba0d0e 160000 --- a/src/support/nss/nss +++ b/src/support/nss/nss @@ -1 +1 @@ -Subproject commit 725cf233061c9598fc7c27cdc1d6278742396a48 +Subproject commit 9d997ba0d0ec7b25c27ad3c8a77587e78ae9f9df From c65d51f812f0355f5fbdb3a64b64c16559196bee Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Thu, 6 Jun 2013 13:55:14 -0700 Subject: [PATCH 30/41] Address review comments --- src/components/main/layout/context.rs | 2 -- src/components/main/layout/layout_task.rs | 2 -- 2 files changed, 4 deletions(-) diff --git a/src/components/main/layout/context.rs b/src/components/main/layout/context.rs index 8646b9faabc..13b6f4e8a83 100644 --- a/src/components/main/layout/context.rs +++ b/src/components/main/layout/context.rs @@ -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 } diff --git a/src/components/main/layout/layout_task.rs b/src/components/main/layout/layout_task.rs index 82a3a214b9c..7856c0fc3dc 100644 --- a/src/components/main/layout/layout_task.rs +++ b/src/components/main/layout/layout_task.rs @@ -116,12 +116,10 @@ impl Layout { let image_cache = self.local_image_cache; let font_ctx = self.font_ctx; let screen_size = self.screen_size.unwrap(); - let doc_url = self.doc_url.clone(); LayoutContext { image_cache: image_cache, font_ctx: font_ctx, - doc_url: doc_url.unwrap(), screen_size: Rect(Point2D(Au(0), Au(0)), screen_size), } } From 98a730c70310416e82703d7684f3a3d489d86ecc Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Thu, 6 Jun 2013 16:21:18 -0700 Subject: [PATCH 31/41] Use full paths for submodules in the configure script --- configure | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/configure b/configure index 5fffd078556..8d39cb8788a 100755 --- a/configure +++ b/configure @@ -503,13 +503,13 @@ 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 = "nspr" ]; then + if [ $i = "support/nss/nspr" ]; then CONFIGURE_ARGS="--enable-64bit" fi - if [ $i = mozjs ]; then + if [ $i = "support/spidermonkey/mozjs" ]; then if [ ! -z $CFG_ENABLE_DEBUG ]; then CONFIGURE_ARGS="--enable-debug" fi From bb6ae7a825d1c9f83f084cdd2648437eb711058d Mon Sep 17 00:00:00 2001 From: Jack Moffitt Date: Mon, 10 Jun 2013 21:02:11 -0600 Subject: [PATCH 32/41] Update NSS to fix build issues. --- src/support/nss/nss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/support/nss/nss b/src/support/nss/nss index 9d997ba0d0e..bf3d0604044 160000 --- a/src/support/nss/nss +++ b/src/support/nss/nss @@ -1 +1 @@ -Subproject commit 9d997ba0d0ec7b25c27ad3c8a77587e78ae9f9df +Subproject commit bf3d060404471c46b474e513b568ac36a92a0b67 From a2c4f72456d51cc02b1100f4ecbbc7984ee7729a Mon Sep 17 00:00:00 2001 From: Jack Moffitt Date: Tue, 11 Jun 2013 13:16:00 -0600 Subject: [PATCH 33/41] Fix spacing. --- mk/sub.mk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mk/sub.mk b/mk/sub.mk index b01f34329e8..63d3d924f71 100644 --- a/mk/sub.mk +++ b/mk/sub.mk @@ -21,8 +21,8 @@ NATIVE_BUILDS += \ libwapcaplet \ mozjs \ skia \ - nss \ - nspr \ + nss \ + nspr \ $(NULL) # NOTE: the make magic can only compute transitive build dependencies, From 4cf4302bd78715392c32bddd9a671dcbda0a80a1 Mon Sep 17 00:00:00 2001 From: Jack Moffitt Date: Tue, 11 Jun 2013 13:16:07 -0600 Subject: [PATCH 34/41] Update nss. --- src/support/nss/nss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/support/nss/nss b/src/support/nss/nss index bf3d0604044..8ba903a1af2 160000 --- a/src/support/nss/nss +++ b/src/support/nss/nss @@ -1 +1 @@ -Subproject commit bf3d060404471c46b474e513b568ac36a92a0b67 +Subproject commit 8ba903a1af20ce461d4f4033ec1092a229bc7482 From ea1c88377c5116e67240f94b1017c5590b554e40 Mon Sep 17 00:00:00 2001 From: Jack Moffitt Date: Tue, 11 Jun 2013 15:29:59 -0600 Subject: [PATCH 35/41] Fix warning. --- src/components/main/layout/block.rs | 2 +- src/support/css/rust-css | 2 +- src/support/glut/rust-glut | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/main/layout/block.rs b/src/components/main/layout/block.rs index 88904d489c3..fc90d3273d2 100644 --- a/src/components/main/layout/block.rs +++ b/src/components/main/layout/block.rs @@ -146,7 +146,7 @@ impl BlockFlowData { //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 )), + (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), diff --git a/src/support/css/rust-css b/src/support/css/rust-css index 09d2db847c1..ca4e2c62e0f 160000 --- a/src/support/css/rust-css +++ b/src/support/css/rust-css @@ -1 +1 @@ -Subproject commit 09d2db847c11bcab7f1832d5daf5947a7c1384ee +Subproject commit ca4e2c62e0f7720d32636b9deaa8af705ce6cdce diff --git a/src/support/glut/rust-glut b/src/support/glut/rust-glut index dd25d69abde..637434c8d3e 160000 --- a/src/support/glut/rust-glut +++ b/src/support/glut/rust-glut @@ -1 +1 @@ -Subproject commit dd25d69abde67e2275fbbbd93196043b69afd67b +Subproject commit 637434c8d3ed968b01a40521a836af76b4e7b967 From 16ca6f2a16effd32f7a69c003f829a89120c1af9 Mon Sep 17 00:00:00 2001 From: Jack Moffitt Date: Tue, 11 Jun 2013 15:30:10 -0600 Subject: [PATCH 36/41] Add missing field. --- src/components/main/layout/box.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/main/layout/box.rs b/src/components/main/layout/box.rs index 6411723731d..78934a937b4 100644 --- a/src/components/main/layout/box.rs +++ b/src/components/main/layout/box.rs @@ -632,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(), From a42cf9b3794e384d6bfb34a4c4d41e06518fb68b Mon Sep 17 00:00:00 2001 From: Jack Moffitt Date: Tue, 11 Jun 2013 15:30:28 -0600 Subject: [PATCH 37/41] Fix types. --- src/components/main/layout/box_builder.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/main/layout/box_builder.rs b/src/components/main/layout/box_builder.rs index 7891ad6e9df..99153d51ee4 100644 --- a/src/components/main/layout/box_builder.rs +++ b/src/components/main/layout/box_builder.rs @@ -466,7 +466,7 @@ pub impl LayoutTreeBuilder { } } if (do_remove) { - parent_flow.remove_child(*first_flow); + (*parent_flow).remove_child(*first_flow); } } } @@ -489,7 +489,7 @@ pub impl LayoutTreeBuilder { } } if (do_remove) { - parent_flow.remove_child(*last_flow); + (*parent_flow).remove_child(*last_flow); } } } From b63507909156e1873e8c7e721dc7eecc8cfae491 Mon Sep 17 00:00:00 2001 From: Jack Moffitt Date: Tue, 11 Jun 2013 15:30:43 -0600 Subject: [PATCH 38/41] Revert rust-css and rust-netsurfcss to working versions. --- src/support/glut/rust-glut | 2 +- src/support/netsurfcss/rust-netsurfcss | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/support/glut/rust-glut b/src/support/glut/rust-glut index 637434c8d3e..dcfc629d3f1 160000 --- a/src/support/glut/rust-glut +++ b/src/support/glut/rust-glut @@ -1 +1 @@ -Subproject commit 637434c8d3ed968b01a40521a836af76b4e7b967 +Subproject commit dcfc629d3f1e8af268e73bf46fb0a82980b6c550 diff --git a/src/support/netsurfcss/rust-netsurfcss b/src/support/netsurfcss/rust-netsurfcss index 8ecca4b5d77..3565b32ba3d 160000 --- a/src/support/netsurfcss/rust-netsurfcss +++ b/src/support/netsurfcss/rust-netsurfcss @@ -1 +1 @@ -Subproject commit 8ecca4b5d779e749a298b1c2ca1152aa2ecd4e79 +Subproject commit 3565b32ba3d15d31b02cc76bdf76d6b13fc88451 From f0692e5795b634753cf8c858ea8819ea6586bf61 Mon Sep 17 00:00:00 2001 From: Jack Moffitt Date: Tue, 11 Jun 2013 16:23:51 -0600 Subject: [PATCH 39/41] Update rust-glut. --- src/support/glut/rust-glut | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/support/glut/rust-glut b/src/support/glut/rust-glut index dcfc629d3f1..453bf81e021 160000 --- a/src/support/glut/rust-glut +++ b/src/support/glut/rust-glut @@ -1 +1 @@ -Subproject commit dcfc629d3f1e8af268e73bf46fb0a82980b6c550 +Subproject commit 453bf81e021008f5eba29b135f07f4529e6c8b2e From a5e3605f4b6028829a0c1ce5d7507b6cbdc8570d Mon Sep 17 00:00:00 2001 From: Jack Moffitt Date: Tue, 11 Jun 2013 16:24:00 -0600 Subject: [PATCH 40/41] Fix rust-css tests. --- src/support/css/rust-css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/support/css/rust-css b/src/support/css/rust-css index ca4e2c62e0f..57407e5209e 160000 --- a/src/support/css/rust-css +++ b/src/support/css/rust-css @@ -1 +1 @@ -Subproject commit ca4e2c62e0f7720d32636b9deaa8af705ce6cdce +Subproject commit 57407e5209ec95e695e17447fa7c5dfea96629b3 From cdd5de7f975aca027d6e71d1cc8dae17e982b73d Mon Sep 17 00:00:00 2001 From: Jack Moffitt Date: Tue, 11 Jun 2013 16:24:10 -0600 Subject: [PATCH 41/41] Disable tests for nspr and nss. --- mk/check.mk | 6 ++++-- mk/sub.mk | 6 ++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/mk/check.mk b/mk/check.mk index 42adc18516d..af8481aaa84 100644 --- a/mk/check.mk +++ b/mk/check.mk @@ -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) diff --git a/mk/sub.mk b/mk/sub.mk index 63d3d924f71..745b5b71179 100644 --- a/mk/sub.mk +++ b/mk/sub.mk @@ -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 \