aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorbors-servo <release+servo@mozilla.com>2014-04-03 21:01:48 -0400
committerbors-servo <release+servo@mozilla.com>2014-04-03 21:01:48 -0400
commit3ec22157ca20227cd2d648744b6c711ed0ae8366 (patch)
tree0df1e9fa97578eef867d6ca60d81df14bf6825c6 /src
parent897c679be28bbb29e248ca4c31ba917cf516e993 (diff)
parent9e3f7a005d4691f5382b757fdab22e3742b6a73f (diff)
downloadservo-3ec22157ca20227cd2d648744b6c711ed0ae8366.tar.gz
servo-3ec22157ca20227cd2d648744b6c711ed0ae8366.zip
auto merge of #1988 : pcwalton/servo/acid2-2, r=SimonSapin
r? @metajack @SimonSapin @larsbergstrom @june0cho @hyunjunekim
Diffstat (limited to 'src')
-rw-r--r--src/components/gfx/display_list.rs263
-rw-r--r--src/components/gfx/render_context.rs12
-rw-r--r--src/components/gfx/render_task.rs372
-rw-r--r--src/components/main/compositing/compositor.rs239
-rw-r--r--src/components/main/compositing/compositor_layer.rs697
-rw-r--r--src/components/main/compositing/compositor_task.rs106
-rw-r--r--src/components/main/compositing/headless.rs9
-rw-r--r--src/components/main/compositing/quadtree.rs16
-rw-r--r--src/components/main/constellation.rs30
-rw-r--r--src/components/main/css/node_util.rs38
-rw-r--r--src/components/main/layout/block.rs1340
-rw-r--r--src/components/main/layout/box_.rs698
-rw-r--r--src/components/main/layout/construct.rs441
-rw-r--r--src/components/main/layout/context.rs2
-rw-r--r--src/components/main/layout/display_list_builder.rs43
-rw-r--r--src/components/main/layout/flow.rs364
-rw-r--r--src/components/main/layout/inline.rs71
-rw-r--r--src/components/main/layout/layout_task.rs174
-rw-r--r--src/components/main/layout/model.rs251
-rw-r--r--src/components/main/layout/parallel.rs9
-rw-r--r--src/components/main/layout/table.rs242
-rw-r--r--src/components/main/layout/table_caption.rs31
-rw-r--r--src/components/main/layout/table_cell.rs90
-rw-r--r--src/components/main/layout/table_colgroup.rs6
-rw-r--r--src/components/main/layout/table_row.rs94
-rw-r--r--src/components/main/layout/table_rowgroup.rs109
-rw-r--r--src/components/main/layout/table_wrapper.rs190
-rw-r--r--src/components/main/layout/util.rs60
-rw-r--r--src/components/main/layout/wrapper.rs262
-rw-r--r--src/components/main/pipeline.rs7
-rw-r--r--src/components/msg/compositor_msg.rs82
-rw-r--r--src/components/script/script_task.rs12
-rw-r--r--src/components/style/common_types.rs4
-rw-r--r--src/components/style/properties.rs.mako409
-rw-r--r--src/components/util/geometry.rs8
-rw-r--r--src/components/util/smallvec.rs40
m---------src/support/azure/rust-azure0
m---------src/support/layers/rust-layers0
-rw-r--r--src/test/ref/background_external_stylesheet.html2
-rw-r--r--src/test/ref/background_position_a.html20
-rw-r--r--src/test/ref/background_position_b.html19
-rw-r--r--src/test/ref/background_ref.html2
-rw-r--r--src/test/ref/background_repeat_both_a.html19
-rw-r--r--src/test/ref/background_repeat_both_b.html27
-rw-r--r--src/test/ref/background_repeat_none_a.html19
-rw-r--r--src/test/ref/background_repeat_none_b.html18
-rw-r--r--src/test/ref/background_repeat_x_a.html22
-rw-r--r--src/test/ref/background_repeat_x_b.html26
-rw-r--r--src/test/ref/background_repeat_y_a.html19
-rw-r--r--src/test/ref/background_repeat_y_b.html21
-rw-r--r--src/test/ref/background_style_attr.html2
-rw-r--r--src/test/ref/basic.list17
-rw-r--r--src/test/ref/inline_padding_a.html16
-rw-r--r--src/test/ref/inline_padding_b.html17
-rw-r--r--src/test/ref/min_max_height_a.html33
-rw-r--r--src/test/ref/min_max_height_b.html29
-rw-r--r--src/test/ref/minimum_line_height_a.html17
-rw-r--r--src/test/ref/minimum_line_height_b.html17
-rw-r--r--src/test/ref/negative_margin_uncle_a.html31
-rw-r--r--src/test/ref/negative_margin_uncle_b.html27
-rw-r--r--src/test/ref/negative_margins_a.html19
-rw-r--r--src/test/ref/negative_margins_b.html25
62 files changed, 4565 insertions, 2720 deletions
diff --git a/src/components/gfx/display_list.rs b/src/components/gfx/display_list.rs
index 34e3d278b3e..abb22e56863 100644
--- a/src/components/gfx/display_list.rs
+++ b/src/components/gfx/display_list.rs
@@ -18,64 +18,142 @@ use color::Color;
use render_context::RenderContext;
use text::TextRun;
-use geom::{Point2D, Rect, Size2D, SideOffsets2D};
+use geom::{Point2D, Rect, SideOffsets2D, Size2D};
use servo_net::image::base::Image;
use servo_util::geometry::Au;
use servo_util::range::Range;
-use std::cast::transmute_region;
+use servo_util::smallvec::{SmallVec, SmallVec0, SmallVecIterator};
+use std::libc::uintptr_t;
+use std::mem;
use std::vec::Items;
use style::computed_values::border_style;
use sync::Arc;
-pub struct DisplayListCollection<E> {
- lists: ~[DisplayList<E>]
+/// An opaque handle to a node. The only safe operation that can be performed on this node is to
+/// compare it to another opaque handle or to another node.
+///
+/// Because the script task's GC does not trace layout, node data cannot be safely stored in layout
+/// data structures. Also, layout code tends to be faster when the DOM is not being accessed, for
+/// locality reasons. Using `OpaqueNode` enforces this invariant.
+#[deriving(Clone, Eq)]
+pub struct OpaqueNode(uintptr_t);
+
+impl OpaqueNode {
+ /// Returns the address of this node, for debugging purposes.
+ pub fn id(&self) -> uintptr_t {
+ let OpaqueNode(pointer) = *self;
+ pointer
+ }
}
-impl<E> DisplayListCollection<E> {
- pub fn new() -> DisplayListCollection<E> {
- DisplayListCollection {
- lists: ~[]
- }
- }
+/// A stacking context. See CSS 2.1 § E.2. "Steps" below refer to steps in that section of the
+/// specification.
+///
+/// TODO(pcwalton): Outlines.
+pub struct StackingContext {
+ /// The border and backgrounds for the root of this stacking context: steps 1 and 2.
+ background_and_borders: DisplayList,
+ /// Borders and backgrounds for block-level descendants: step 4.
+ block_backgrounds_and_borders: DisplayList,
+ /// Floats: step 5. These are treated as pseudo-stacking contexts.
+ floats: DisplayList,
+ /// All other content.
+ content: DisplayList,
+
+ /// Positioned descendant stacking contexts, along with their `z-index` levels.
+ ///
+ /// TODO(pcwalton): `z-index` should be the actual CSS property value in order to handle
+ /// `auto`, not just an integer. In this case we should store an actual stacking context, not
+ /// a flattened display list.
+ positioned_descendants: SmallVec0<(int, DisplayList)>,
+}
- pub fn iter<'a>(&'a self) -> DisplayListIterator<'a,E> {
- ParentDisplayListIterator(self.lists.iter())
+impl StackingContext {
+ pub fn new() -> StackingContext {
+ StackingContext {
+ background_and_borders: DisplayList::new(),
+ block_backgrounds_and_borders: DisplayList::new(),
+ floats: DisplayList::new(),
+ content: DisplayList::new(),
+ positioned_descendants: SmallVec0::new(),
+ }
}
- pub fn add_list(&mut self, list: DisplayList<E>) {
- self.lists.push(list);
+ pub fn list_for_background_and_border_level<'a>(
+ &'a mut self,
+ level: BackgroundAndBorderLevel)
+ -> &'a mut DisplayList {
+ match level {
+ RootOfStackingContextLevel => &mut self.background_and_borders,
+ BlockLevel => &mut self.block_backgrounds_and_borders,
+ ContentLevel => &mut self.content,
+ }
}
- pub fn draw_lists_into_context(&self, render_context: &mut RenderContext) {
- for list in self.lists.iter() {
- list.draw_into_context(render_context);
+ /// Flattens a stacking context into a display list according to the steps in CSS 2.1 § E.2.
+ pub fn flatten(self) -> DisplayList {
+ // Steps 1 and 2: Borders and background for the root.
+ let StackingContext {
+ background_and_borders: mut result,
+ block_backgrounds_and_borders,
+ floats,
+ content,
+ positioned_descendants: mut positioned_descendants
+ } = self;
+
+ // TODO(pcwalton): Sort positioned children according to z-index.
+
+ // Step 3: Positioned descendants with negative z-indices.
+ for &(ref mut z_index, ref mut list) in positioned_descendants.mut_iter() {
+ if *z_index < 0 {
+ result.push_all_move(mem::replace(list, DisplayList::new()))
+ }
}
- debug!("{:?}", self.dump());
- }
- fn dump(&self) {
- let mut index = 0;
- for list in self.lists.iter() {
- debug!("dumping display list {:d}:", index);
- list.dump();
- index = index + 1;
+ // Step 4: Block backgrounds and borders.
+ result.push_all_move(block_backgrounds_and_borders);
+
+ // Step 5: Floats.
+ result.push_all_move(floats);
+
+ // TODO(pcwalton): Step 6: Inlines that generate stacking contexts.
+
+ // Step 7: Content.
+ result.push_all_move(content);
+
+ // Steps 8 and 9: Positioned descendants with nonnegative z-indices.
+ for &(ref mut z_index, ref mut list) in positioned_descendants.mut_iter() {
+ if *z_index >= 0 {
+ result.push_all_move(mem::replace(list, DisplayList::new()))
+ }
}
+
+ // TODO(pcwalton): Step 10: Outlines.
+
+ result
}
}
+/// Which level to place backgrounds and borders in.
+pub enum BackgroundAndBorderLevel {
+ RootOfStackingContextLevel,
+ BlockLevel,
+ ContentLevel,
+}
+
/// A list of rendering operations to be performed.
-pub struct DisplayList<E> {
- list: ~[DisplayItem<E>]
+pub struct DisplayList {
+ list: SmallVec0<DisplayItem>,
}
-pub enum DisplayListIterator<'a,E> {
+pub enum DisplayListIterator<'a> {
EmptyDisplayListIterator,
- ParentDisplayListIterator(Items<'a,DisplayList<E>>),
+ ParentDisplayListIterator(Items<'a,DisplayList>),
}
-impl<'a,E> Iterator<&'a DisplayList<E>> for DisplayListIterator<'a,E> {
+impl<'a> Iterator<&'a DisplayList> for DisplayListIterator<'a> {
#[inline]
- fn next(&mut self) -> Option<&'a DisplayList<E>> {
+ fn next(&mut self) -> Option<&'a DisplayList> {
match *self {
EmptyDisplayListIterator => None,
ParentDisplayListIterator(ref mut subiterator) => subiterator.next(),
@@ -83,11 +161,11 @@ impl<'a,E> Iterator<&'a DisplayList<E>> for DisplayListIterator<'a,E> {
}
}
-impl<E> DisplayList<E> {
+impl DisplayList {
/// Creates a new display list.
- pub fn new() -> DisplayList<E> {
+ pub fn new() -> DisplayList {
DisplayList {
- list: ~[]
+ list: SmallVec0::new(),
}
}
@@ -98,60 +176,62 @@ impl<E> DisplayList<E> {
}
/// Appends the given item to the display list.
- pub fn append_item(&mut self, item: DisplayItem<E>) {
- // FIXME(Issue #150): crashes
- //debug!("Adding display item {:u}: {}", self.len(), item);
+ pub fn push(&mut self, item: DisplayItem) {
self.list.push(item)
}
+ /// Appends the given display list to this display list, consuming the other display list in
+ /// the process.
+ pub fn push_all_move(&mut self, other: DisplayList) {
+ self.list.push_all_move(other.list)
+ }
+
/// Draws the display list into the given render context.
pub fn draw_into_context(&self, render_context: &mut RenderContext) {
debug!("Beginning display list.");
for item in self.list.iter() {
- // FIXME(Issue #150): crashes
- //debug!("drawing {}", *item);
item.draw_into_context(render_context)
}
debug!("Ending display list.");
}
/// Returns a preorder iterator over the given display list.
- pub fn iter<'a>(&'a self) -> DisplayItemIterator<'a,E> {
+ pub fn iter<'a>(&'a self) -> DisplayItemIterator<'a> {
ParentDisplayItemIterator(self.list.iter())
}
}
/// One drawing command in the list.
-pub enum DisplayItem<E> {
- SolidColorDisplayItemClass(~SolidColorDisplayItem<E>),
- TextDisplayItemClass(~TextDisplayItem<E>),
- ImageDisplayItemClass(~ImageDisplayItem<E>),
- BorderDisplayItemClass(~BorderDisplayItem<E>),
- LineDisplayItemClass(~LineDisplayItem<E>),
- ClipDisplayItemClass(~ClipDisplayItem<E>)
+pub enum DisplayItem {
+ SolidColorDisplayItemClass(~SolidColorDisplayItem),
+ TextDisplayItemClass(~TextDisplayItem),
+ ImageDisplayItemClass(~ImageDisplayItem),
+ BorderDisplayItemClass(~BorderDisplayItem),
+ LineDisplayItemClass(~LineDisplayItem),
+ ClipDisplayItemClass(~ClipDisplayItem)
}
/// Information common to all display items.
-pub struct BaseDisplayItem<E> {
+pub struct BaseDisplayItem {
/// The boundaries of the display item.
///
/// TODO: Which coordinate system should this use?
bounds: Rect<Au>,
- /// Extra data: either the originating flow (for hit testing) or nothing (for rendering).
- extra: E,
+ /// The originating DOM node.
+ node: OpaqueNode,
}
/// Renders a solid color.
-pub struct SolidColorDisplayItem<E> {
- base: BaseDisplayItem<E>,
+pub struct SolidColorDisplayItem {
+ base: BaseDisplayItem,
color: Color,
}
/// Renders text.
-pub struct TextDisplayItem<E> {
+pub struct TextDisplayItem {
/// Fields common to all display items.
- base: BaseDisplayItem<E>,
+ base: BaseDisplayItem,
/// The text run.
text_run: Arc<~TextRun>,
@@ -188,14 +268,19 @@ bitfield!(TextDisplayItemFlags, override_overline, set_override_overline, 0x02)
bitfield!(TextDisplayItemFlags, override_line_through, set_override_line_through, 0x04)
/// Renders an image.
-pub struct ImageDisplayItem<E> {
- base: BaseDisplayItem<E>,
+pub struct ImageDisplayItem {
+ base: BaseDisplayItem,
image: Arc<~Image>,
+
+ /// The dimensions to which the image display item should be stretched. If this is smaller than
+ /// the bounds of this display item, then the image will be repeated in the appropriate
+ /// direction to tile the entire bounds.
+ stretch_size: Size2D<Au>,
}
/// Renders a border.
-pub struct BorderDisplayItem<E> {
- base: BaseDisplayItem<E>,
+pub struct BorderDisplayItem {
+ base: BaseDisplayItem,
/// The border widths
border: SideOffsets2D<Au>,
@@ -207,31 +292,31 @@ pub struct BorderDisplayItem<E> {
style: SideOffsets2D<border_style::T>
}
-/// Renders a line segment
-pub struct LineDisplayItem<E> {
- base: BaseDisplayItem<E>,
+/// Renders a line segment.
+pub struct LineDisplayItem {
+ base: BaseDisplayItem,
/// The line segment color.
color: Color,
- /// The line segemnt style.
+ /// The line segment style.
style: border_style::T
}
-pub struct ClipDisplayItem<E> {
- base: BaseDisplayItem<E>,
- child_list: ~[DisplayItem<E>],
+pub struct ClipDisplayItem {
+ base: BaseDisplayItem,
+ child_list: SmallVec0<DisplayItem>,
need_clip: bool
}
-pub enum DisplayItemIterator<'a,E> {
+pub enum DisplayItemIterator<'a> {
EmptyDisplayItemIterator,
- ParentDisplayItemIterator(Items<'a,DisplayItem<E>>),
+ ParentDisplayItemIterator(SmallVecIterator<'a,DisplayItem>),
}
-impl<'a,E> Iterator<&'a DisplayItem<E>> for DisplayItemIterator<'a,E> {
+impl<'a> Iterator<&'a DisplayItem> for DisplayItemIterator<'a> {
#[inline]
- fn next(&mut self) -> Option<&'a DisplayItem<E>> {
+ fn next(&mut self) -> Option<&'a DisplayItem> {
match *self {
EmptyDisplayItemIterator => None,
ParentDisplayItemIterator(ref mut subiterator) => subiterator.next(),
@@ -239,7 +324,7 @@ impl<'a,E> Iterator<&'a DisplayItem<E>> for DisplayItemIterator<'a,E> {
}
}
-impl<E> DisplayItem<E> {
+impl DisplayItem {
/// Renders this display item into the given render context.
fn draw_into_context(&self, render_context: &mut RenderContext) {
match *self {
@@ -306,7 +391,22 @@ impl<E> DisplayItem<E> {
ImageDisplayItemClass(ref image_item) => {
debug!("Drawing image at {:?}.", image_item.base.bounds);
- render_context.draw_image(image_item.base.bounds, image_item.image.clone())
+ let mut y_offset = Au(0);
+ while y_offset < image_item.base.bounds.size.height {
+ let mut x_offset = Au(0);
+ while x_offset < image_item.base.bounds.size.width {
+ let mut bounds = image_item.base.bounds;
+ bounds.origin.x = bounds.origin.x + x_offset;
+ bounds.origin.y = bounds.origin.y + y_offset;
+ bounds.size = image_item.stretch_size;
+
+ render_context.draw_image(bounds, image_item.image.clone());
+
+ x_offset = x_offset + image_item.stretch_size.width;
+ }
+
+ y_offset = y_offset + image_item.stretch_size.height;
+ }
}
BorderDisplayItemClass(ref border) => {
@@ -324,17 +424,14 @@ impl<E> DisplayItem<E> {
}
}
- pub fn base<'a>(&'a self) -> &'a BaseDisplayItem<E> {
- // FIXME(tkuehn): Workaround for Rust region bug.
- unsafe {
- match *self {
- SolidColorDisplayItemClass(ref solid_color) => transmute_region(&solid_color.base),
- TextDisplayItemClass(ref text) => transmute_region(&text.base),
- ImageDisplayItemClass(ref image_item) => transmute_region(&image_item.base),
- BorderDisplayItemClass(ref border) => transmute_region(&border.base),
- LineDisplayItemClass(ref line) => transmute_region(&line.base),
- ClipDisplayItemClass(ref clip) => transmute_region(&clip.base),
- }
+ pub fn base<'a>(&'a self) -> &'a BaseDisplayItem {
+ match *self {
+ SolidColorDisplayItemClass(ref solid_color) => &solid_color.base,
+ TextDisplayItemClass(ref text) => &text.base,
+ ImageDisplayItemClass(ref image_item) => &image_item.base,
+ BorderDisplayItemClass(ref border) => &border.base,
+ LineDisplayItemClass(ref line) => &line.base,
+ ClipDisplayItemClass(ref clip) => &clip.base,
}
}
@@ -342,7 +439,7 @@ impl<E> DisplayItem<E> {
self.base().bounds
}
- pub fn children<'a>(&'a self) -> DisplayItemIterator<'a,E> {
+ pub fn children<'a>(&'a self) -> DisplayItemIterator<'a> {
match *self {
ClipDisplayItemClass(ref clip) => ParentDisplayItemIterator(clip.child_list.iter()),
SolidColorDisplayItemClass(..) |
diff --git a/src/components/gfx/render_context.rs b/src/components/gfx/render_context.rs
index a33d8f1f085..44529c057e3 100644
--- a/src/components/gfx/render_context.rs
+++ b/src/components/gfx/render_context.rs
@@ -5,8 +5,8 @@
use font_context::FontContext;
use style::computed_values::border_style;
-use azure::azure_hl::{B8G8R8A8, Color, ColorPattern, DrawOptions};
-use azure::azure_hl::{DrawSurfaceOptions, DrawTarget, Linear, StrokeOptions};
+use azure::azure_hl::{B8G8R8A8, Color, ColorPattern, DrawOptions, DrawSurfaceOptions, DrawTarget};
+use azure::azure_hl::{Linear, SourceOp, StrokeOptions};
use azure::AZ_CAP_BUTT;
use azure::AzFloat;
use geom::point::Point2D;
@@ -45,7 +45,7 @@ impl<'a> RenderContext<'a> {
pub fn draw_solid_color(&self, bounds: &Rect<Au>, color: Color) {
self.draw_target.make_current();
- self.draw_target.fill_rect(&bounds.to_azure_rect(), &ColorPattern(color));
+ self.draw_target.fill_rect(&bounds.to_azure_rect(), &ColorPattern(color), None);
}
pub fn draw_border(&self,
@@ -121,13 +121,15 @@ impl<'a> RenderContext<'a> {
}
pub fn clear(&self) {
- let pattern = ColorPattern(Color(1.0, 1.0, 1.0, 1.0));
+ let pattern = ColorPattern(Color(0.0, 0.0, 0.0, 0.0));
let rect = Rect(Point2D(self.page_rect.origin.x as AzFloat,
self.page_rect.origin.y as AzFloat),
Size2D(self.screen_rect.size.width as AzFloat,
self.screen_rect.size.height as AzFloat));
+ let mut draw_options = DrawOptions(1.0, 0);
+ draw_options.set_composition_op(SourceOp);
self.draw_target.make_current();
- self.draw_target.fill_rect(&rect, &pattern);
+ self.draw_target.fill_rect(&rect, &pattern, Some(&draw_options));
}
fn draw_border_segment(&self, direction: Direction, bounds: &Rect<Au>, border: SideOffsets2D<f32>, color: SideOffsets2D<Color>, style: SideOffsets2D<border_style::T>) {
diff --git a/src/components/gfx/render_task.rs b/src/components/gfx/render_task.rs
index bb9ed1f665a..24e3580eb33 100644
--- a/src/components/gfx/render_task.rs
+++ b/src/components/gfx/render_task.rs
@@ -2,7 +2,12 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-// The task that handles all rendering/painting.
+//! The task that handles all rendering/painting.
+
+use buffer_map::BufferMap;
+use display_list::DisplayList;
+use font_context::{FontContext, FontContextInfo};
+use render_context::RenderContext;
use azure::azure_hl::{B8G8R8A8, Color, DrawTarget, StolenGLResources};
use azure::AzFloat;
@@ -12,12 +17,14 @@ use geom::size::Size2D;
use layers::platform::surface::{NativePaintingGraphicsContext, NativeSurface};
use layers::platform::surface::{NativeSurfaceMethods};
use layers;
-use servo_msg::compositor_msg::{Epoch, IdleRenderState, LayerBuffer, LayerBufferSet};
-use servo_msg::compositor_msg::{RenderListener, RenderingRenderState};
+use servo_msg::compositor_msg::{Epoch, IdleRenderState, LayerBuffer};
+use servo_msg::compositor_msg::{LayerBufferSet, LayerId, LayerMetadata, RenderListener};
+use servo_msg::compositor_msg::{RenderingRenderState, ScrollPolicy};
use servo_msg::constellation_msg::{ConstellationChan, PipelineId, RendererReadyMsg};
use servo_msg::constellation_msg::{Failure, FailureMsg};
use servo_msg::platform::surface::NativeSurfaceAzureMethods;
use servo_util::opts::Opts;
+use servo_util::smallvec::{SmallVec, SmallVec1};
use servo_util::time::{ProfilerChan, profile};
use servo_util::time;
use servo_util::task::send_on_failure;
@@ -26,20 +33,23 @@ use std::comm::{Chan, Port};
use std::task;
use sync::Arc;
-use buffer_map::BufferMap;
-use display_list::DisplayListCollection;
-use font_context::{FontContext, FontContextInfo};
-use render_context::RenderContext;
-
-pub struct RenderLayer<T> {
- display_list_collection: Arc<DisplayListCollection<T>>,
- size: Size2D<uint>,
- color: Color
+/// Information about a layer that layout sends to the painting task.
+pub struct RenderLayer {
+ /// A per-pipeline ID describing this layer that should be stable across reflows.
+ id: LayerId,
+ /// The display list describing the contents of this layer.
+ display_list: Arc<DisplayList>,
+ /// The position of the layer in pixels.
+ position: Rect<uint>,
+ /// The color of the background in this layer. Used for unrendered content.
+ background_color: Color,
+ /// The scrolling policy of this layer.
+ scroll_policy: ScrollPolicy,
}
-pub enum Msg<T> {
- RenderMsg(RenderLayer<T>),
- ReRenderMsg(~[BufferRequest], f32, Epoch),
+pub enum Msg {
+ RenderMsg(SmallVec1<RenderLayer>),
+ ReRenderMsg(~[BufferRequest], f32, LayerId, Epoch),
UnusedBufferMsg(~[~LayerBuffer]),
PaintPermissionGranted,
PaintPermissionRevoked,
@@ -63,22 +73,21 @@ pub fn BufferRequest(screen_rect: Rect<uint>, page_rect: Rect<f32>) -> BufferReq
}
}
-// FIXME(rust#9155): this should be a newtype struct, but
-// generic newtypes ICE when compiled cross-crate
-pub struct RenderChan<T> {
- chan: Chan<Msg<T>>,
+// FIXME(#2005, pcwalton): This should be a newtype struct.
+pub struct RenderChan {
+ chan: Chan<Msg>,
}
-impl<T: Send> Clone for RenderChan<T> {
- fn clone(&self) -> RenderChan<T> {
+impl Clone for RenderChan {
+ fn clone(&self) -> RenderChan {
RenderChan {
chan: self.chan.clone(),
}
}
}
-impl<T: Send> RenderChan<T> {
- pub fn new() -> (Port<Msg<T>>, RenderChan<T>) {
+impl RenderChan {
+ pub fn new() -> (Port<Msg>, RenderChan) {
let (port, chan) = Chan::new();
let render_chan = RenderChan {
chan: chan,
@@ -86,11 +95,11 @@ impl<T: Send> RenderChan<T> {
(port, render_chan)
}
- pub fn send(&self, msg: Msg<T>) {
+ pub fn send(&self, msg: Msg) {
assert!(self.try_send(msg), "RenderChan.send: render port closed")
}
- pub fn try_send(&self, msg: Msg<T>) -> bool {
+ pub fn try_send(&self, msg: Msg) -> bool {
self.chan.try_send(msg)
}
}
@@ -102,9 +111,9 @@ enum GraphicsContext {
GpuGraphicsContext,
}
-pub struct RenderTask<C,T> {
+pub struct RenderTask<C> {
id: PipelineId,
- port: Port<Msg<T>>,
+ port: Port<Msg>,
compositor: C,
constellation_chan: ConstellationChan,
font_ctx: ~FontContext,
@@ -119,8 +128,8 @@ pub struct RenderTask<C,T> {
/// The native graphics context.
native_graphics_context: Option<NativePaintingGraphicsContext>,
- /// The layer to be rendered
- render_layer: Option<RenderLayer<T>>,
+ /// The layers to be rendered.
+ render_layers: SmallVec1<RenderLayer>,
/// Permission to send paint messages to the compositor
paint_permission: bool,
@@ -140,9 +149,25 @@ macro_rules! native_graphics_context(
)
)
-impl<C: RenderListener + Send,T:Send+Freeze> RenderTask<C,T> {
+fn initialize_layers<C:RenderListener>(
+ compositor: &mut C,
+ pipeline_id: PipelineId,
+ epoch: Epoch,
+ render_layers: &[RenderLayer]) {
+ let metadata = render_layers.iter().map(|render_layer| {
+ LayerMetadata {
+ id: render_layer.id,
+ position: render_layer.position,
+ background_color: render_layer.background_color,
+ scroll_policy: render_layer.scroll_policy,
+ }
+ }).collect();
+ compositor.initialize_layers_for_pipeline(pipeline_id, metadata, epoch);
+}
+
+impl<C: RenderListener + Send> RenderTask<C> {
pub fn create(id: PipelineId,
- port: Port<Msg<T>>,
+ port: Port<Msg>,
compositor: C,
constellation_chan: ConstellationChan,
failure_msg: Failure,
@@ -181,7 +206,7 @@ impl<C: RenderListener + Send,T:Send+Freeze> RenderTask<C,T> {
native_graphics_context: native_graphics_context,
- render_layer: None,
+ render_layers: SmallVec1::new(),
paint_permission: false,
epoch: Epoch(0),
@@ -207,20 +232,25 @@ impl<C: RenderListener + Send,T:Send+Freeze> RenderTask<C,T> {
loop {
match self.port.recv() {
- RenderMsg(render_layer) => {
- if self.paint_permission {
- self.epoch.next();
- self.compositor.set_layer_page_size_and_color(self.id, render_layer.size, self.epoch, render_layer.color);
- } else {
+ RenderMsg(render_layers) => {
+ self.epoch.next();
+ self.render_layers = render_layers;
+
+ if !self.paint_permission {
debug!("render_task: render ready msg");
let ConstellationChan(ref mut c) = self.constellation_chan;
c.send(RendererReadyMsg(self.id));
+ continue;
}
- self.render_layer = Some(render_layer);
+
+ initialize_layers(&mut self.compositor,
+ self.id,
+ self.epoch,
+ self.render_layers.as_slice());
}
- ReRenderMsg(tiles, scale, epoch) => {
+ ReRenderMsg(tiles, scale, layer_id, epoch) => {
if self.epoch == epoch {
- self.render(tiles, scale);
+ self.render(tiles, scale, layer_id);
} else {
debug!("renderer epoch mismatch: {:?} != {:?}", self.epoch, epoch);
}
@@ -233,12 +263,16 @@ impl<C: RenderListener + Send,T:Send+Freeze> RenderTask<C,T> {
}
PaintPermissionGranted => {
self.paint_permission = true;
- match self.render_layer {
- Some(ref render_layer) => {
- self.epoch.next();
- self.compositor.set_layer_page_size_and_color(self.id, render_layer.size, self.epoch, render_layer.color);
- }
- None => {}
+
+ // Here we assume that the main layer—the layer responsible for the page size—
+ // is the first layer. This is a pretty fragile assumption. It will be fixed
+ // once we use the layers-based scrolling infrastructure for all scrolling.
+ if self.render_layers.len() > 1 {
+ self.epoch.next();
+ initialize_layers(&mut self.compositor,
+ self.id,
+ self.epoch,
+ self.render_layers.as_slice());
}
}
PaintPermissionRevoked => {
@@ -253,138 +287,144 @@ impl<C: RenderListener + Send,T:Send+Freeze> RenderTask<C,T> {
}
}
- fn render(&mut self, tiles: ~[BufferRequest], scale: f32) {
- if self.render_layer.is_none() {
- return
- }
-
- self.compositor.set_render_state(RenderingRenderState);
+ /// Renders one layer and sends the tiles back to the layer.
+ ///
+ /// FIXME(pcwalton): We will probably want to eventually send all layers belonging to a page in
+ /// one transaction, to avoid the user seeing inconsistent states.
+ fn render(&mut self, tiles: ~[BufferRequest], scale: f32, layer_id: LayerId) {
time::profile(time::RenderingCategory, self.profiler_chan.clone(), || {
// FIXME: Try not to create a new array here.
let mut new_buffers = ~[];
+ // Find the appropriate render layer.
+ let render_layer = match self.render_layers.iter().find(|layer| layer.id == layer_id) {
+ Some(render_layer) => render_layer,
+ None => return,
+ };
+
+ self.compositor.set_render_state(RenderingRenderState);
+
// Divide up the layer into tiles.
- time::profile(time::RenderingPrepBuffCategory, self.profiler_chan.clone(), || {
- for tile in tiles.iter() {
- let width = tile.screen_rect.size.width;
- let height = tile.screen_rect.size.height;
-
- let size = Size2D(width as i32, height as i32);
- let draw_target = match self.graphics_context {
- CpuGraphicsContext => {
- DrawTarget::new(self.opts.render_backend, size, B8G8R8A8)
- }
- GpuGraphicsContext => {
- // FIXME(pcwalton): Cache the components of draw targets
- // (texture color buffer, renderbuffers) instead of recreating them.
- let draw_target =
- DrawTarget::new_with_fbo(self.opts.render_backend,
- native_graphics_context!(self),
- size,
- B8G8R8A8);
- draw_target.make_current();
- draw_target
- }
+ for tile in tiles.iter() {
+ let width = tile.screen_rect.size.width;
+ let height = tile.screen_rect.size.height;
+
+ let size = Size2D(width as i32, height as i32);
+ let draw_target = match self.graphics_context {
+ CpuGraphicsContext => {
+ DrawTarget::new(self.opts.render_backend, size, B8G8R8A8)
+ }
+ GpuGraphicsContext => {
+ // FIXME(pcwalton): Cache the components of draw targets
+ // (texture color buffer, renderbuffers) instead of recreating them.
+ let draw_target =
+ DrawTarget::new_with_fbo(self.opts.render_backend,
+ native_graphics_context!(self),
+ size,
+ B8G8R8A8);
+ draw_target.make_current();
+ draw_target
+ }
+ };
+
+ {
+ // Build the render context.
+ let mut ctx = RenderContext {
+ draw_target: &draw_target,
+ font_ctx: &mut self.font_ctx,
+ opts: &self.opts,
+ page_rect: tile.page_rect,
+ screen_rect: tile.screen_rect,
};
- {
- // Build the render context.
- let mut ctx = RenderContext {
- draw_target: &draw_target,
- font_ctx: &mut self.font_ctx,
- opts: &self.opts,
- page_rect: tile.page_rect,
- screen_rect: tile.screen_rect,
- };
+ // Apply the translation to render the tile we want.
+ let matrix: Matrix2D<AzFloat> = Matrix2D::identity();
+ let matrix = matrix.scale(scale as AzFloat, scale as AzFloat);
+ let matrix = matrix.translate(-(tile.page_rect.origin.x) as AzFloat,
+ -(tile.page_rect.origin.y) as AzFloat);
+ let matrix = matrix.translate(-(render_layer.position.origin.x as AzFloat),
+ -(render_layer.position.origin.y as AzFloat));
- // Apply the translation to render the tile we want.
- let matrix: Matrix2D<AzFloat> = Matrix2D::identity();
- let matrix = matrix.scale(scale as AzFloat, scale as AzFloat);
- let matrix = matrix.translate(-(tile.page_rect.origin.x) as AzFloat,
- -(tile.page_rect.origin.y) as AzFloat);
-
- ctx.draw_target.set_transform(&matrix);
-
- // Clear the buffer.
- ctx.clear();
-
- // Draw the display list.
- profile(time::RenderingDrawingCategory, self.profiler_chan.clone(), || {
- let render_layer = self.render_layer.as_ref().unwrap();
- render_layer.display_list_collection.get().draw_lists_into_context(&mut ctx);
- ctx.draw_target.flush();
- });
- }
+ ctx.draw_target.set_transform(&matrix);
- // Extract the texture from the draw target and place it into its slot in the
- // buffer. If using CPU rendering, upload it first.
- //
- // FIXME(pcwalton): We should supply the texture and native surface *to* the
- // draw target in GPU rendering mode, so that it doesn't have to recreate it.
- let buffer = match self.graphics_context {
- CpuGraphicsContext => {
- let buffer = match self.buffer_map.find(tile.screen_rect.size) {
- Some(buffer) => {
- let mut buffer = buffer;
- buffer.rect = tile.page_rect;
- buffer.screen_pos = tile.screen_rect;
- buffer.resolution = scale;
- buffer.native_surface.mark_wont_leak();
- buffer
- }
- None => {
- // Create an empty native surface. We mark it as not leaking
- // in case it dies in transit to the compositor task.
- let mut native_surface: NativeSurface =
- layers::platform::surface::NativeSurfaceMethods::new(
- native_graphics_context!(self),
- Size2D(width as i32, height as i32),
- width as i32 * 4);
- native_surface.mark_wont_leak();
-
- ~LayerBuffer {
- native_surface: native_surface,
- rect: tile.page_rect,
- screen_pos: tile.screen_rect,
- resolution: scale,
- stride: (width * 4) as uint
- }
- }
- };
+ // Clear the buffer.
+ ctx.clear();
- draw_target.snapshot().get_data_surface().with_data(|data| {
- buffer.native_surface.upload(native_graphics_context!(self), data);
- debug!("RENDERER uploading to native surface {:d}",
- buffer.native_surface.get_id() as int);
- });
+ // Draw the display list.
+ profile(time::RenderingDrawingCategory, self.profiler_chan.clone(), || {
+ render_layer.display_list.get().draw_into_context(&mut ctx);
+ ctx.draw_target.flush();
+ });
+ }
- buffer
- }
- GpuGraphicsContext => {
- draw_target.make_current();
- let StolenGLResources {
- surface: native_surface
- } = draw_target.steal_gl_resources().unwrap();
-
- // We mark the native surface as not leaking in case the surfaces
- // die on their way to the compositor task.
- let mut native_surface: NativeSurface =
- NativeSurfaceAzureMethods::from_azure_surface(native_surface);
- native_surface.mark_wont_leak();
-
- ~LayerBuffer {
- native_surface: native_surface,
- rect: tile.page_rect,
- screen_pos: tile.screen_rect,
- resolution: scale,
- stride: (width * 4) as uint
+ // Extract the texture from the draw target and place it into its slot in the
+ // buffer. If using CPU rendering, upload it first.
+ //
+ // FIXME(pcwalton): We should supply the texture and native surface *to* the
+ // draw target in GPU rendering mode, so that it doesn't have to recreate it.
+ let buffer = match self.graphics_context {
+ CpuGraphicsContext => {
+ let buffer = match self.buffer_map.find(tile.screen_rect.size) {
+ Some(buffer) => {
+ let mut buffer = buffer;
+ buffer.rect = tile.page_rect;
+ buffer.screen_pos = tile.screen_rect;
+ buffer.resolution = scale;
+ buffer.native_surface.mark_wont_leak();
+ buffer
+ }
+ None => {
+ // Create an empty native surface. We mark it as not leaking
+ // in case it dies in transit to the compositor task.
+ let mut native_surface: NativeSurface =
+ layers::platform::surface::NativeSurfaceMethods::new(
+ native_graphics_context!(self),
+ Size2D(width as i32, height as i32),
+ width as i32 * 4);
+ native_surface.mark_wont_leak();
+
+ ~LayerBuffer {
+ native_surface: native_surface,
+ rect: tile.page_rect,
+ screen_pos: tile.screen_rect,
+ resolution: scale,
+ stride: (width * 4) as uint
+ }
}
+ };
+
+ draw_target.snapshot().get_data_surface().with_data(|data| {
+ buffer.native_surface.upload(native_graphics_context!(self), data);
+ debug!("RENDERER uploading to native surface {:d}",
+ buffer.native_surface.get_id() as int);
+ });
+
+ buffer
+ }
+ GpuGraphicsContext => {
+ draw_target.make_current();
+ let StolenGLResources {
+ surface: native_surface
+ } = draw_target.steal_gl_resources().unwrap();
+
+ // We mark the native surface as not leaking in case the surfaces
+ // die on their way to the compositor task.
+ let mut native_surface: NativeSurface =
+ NativeSurfaceAzureMethods::from_azure_surface(native_surface);
+ native_surface.mark_wont_leak();
+
+ ~LayerBuffer {
+ native_surface: native_surface,
+ rect: tile.page_rect,
+ screen_pos: tile.screen_rect,
+ resolution: scale,
+ stride: (width * 4) as uint
}
- };
-
- new_buffers.push(buffer);
- }
- });
+ }
+ };
+
+ new_buffers.push(buffer);
+ }
let layer_buffer_set = ~LayerBufferSet {
buffers: new_buffers,
@@ -392,7 +432,7 @@ impl<C: RenderListener + Send,T:Send+Freeze> RenderTask<C,T> {
debug!("render_task: returning surface");
if self.paint_permission {
- self.compositor.paint(self.id, layer_buffer_set, self.epoch);
+ self.compositor.paint(self.id, render_layer.id, layer_buffer_set, self.epoch);
} else {
debug!("render_task: RendererReadyMsg send");
let ConstellationChan(ref mut c) = self.constellation_chan;
diff --git a/src/components/main/compositing/compositor.rs b/src/components/main/compositing/compositor.rs
index 70655817fee..fe4e386eaae 100644
--- a/src/components/main/compositing/compositor.rs
+++ b/src/components/main/compositing/compositor.rs
@@ -5,16 +5,13 @@
use constellation::SendableFrameTree;
use compositing::compositor_layer::CompositorLayer;
use compositing::*;
-
+use pipeline::CompositionPipeline;
use platform::{Application, Window};
-
-use windowing::{WindowEvent, WindowMethods,
- WindowNavigateMsg,
- IdleWindowEvent, RefreshWindowEvent, ResizeWindowEvent, LoadUrlWindowEvent,
- MouseWindowEventClass, MouseWindowMoveEventClass,ScrollWindowEvent, ZoomWindowEvent, NavigationWindowEvent,
- FinishedWindowEvent, QuitWindowEvent,
- MouseWindowEvent, MouseWindowClickEvent, MouseWindowMouseDownEvent, MouseWindowMouseUpEvent};
-
+use windowing::{FinishedWindowEvent, IdleWindowEvent, LoadUrlWindowEvent, MouseWindowClickEvent};
+use windowing::{MouseWindowEvent, MouseWindowEventClass, MouseWindowMouseDownEvent};
+use windowing::{MouseWindowMouseUpEvent, MouseWindowMoveEventClass, NavigationWindowEvent};
+use windowing::{QuitWindowEvent, RefreshWindowEvent, ResizeWindowEvent, ScrollWindowEvent};
+use windowing::{WindowEvent, WindowMethods, WindowNavigateMsg, ZoomWindowEvent};
use azure::azure_hl::{SourceSurfaceMethods, Color};
use azure::azure_hl;
@@ -29,8 +26,10 @@ use layers::rendergl::RenderContext;
use layers::scene::Scene;
use opengles::gl2;
use png;
-use servo_msg::compositor_msg::{Blank, Epoch, FinishedLoading, IdleRenderState, LayerBufferSet, ReadyState, RenderState};
-use servo_msg::constellation_msg::{ConstellationChan, ExitMsg, NavigateMsg, ResizedWindowMsg, LoadUrlMsg, PipelineId};
+use servo_msg::compositor_msg::{Blank, Epoch, FinishedLoading, IdleRenderState, LayerBufferSet};
+use servo_msg::compositor_msg::{LayerId, ReadyState, RenderState, ScrollPolicy, Scrollable};
+use servo_msg::constellation_msg::{ConstellationChan, ExitMsg, LoadUrlMsg, NavigateMsg};
+use servo_msg::constellation_msg::{PipelineId, ResizedWindowMsg};
use servo_msg::constellation_msg;
use servo_util::opts::Opts;
use servo_util::time::{profile, ProfilerChan, Timer};
@@ -55,6 +54,9 @@ pub struct IOCompositor {
/// The root ContainerLayer.
root_layer: Rc<ContainerLayer>,
+ /// The root pipeline.
+ root_pipeline: Option<CompositionPipeline>,
+
/// The canvas to paint a page.
scene: Scene,
@@ -110,7 +112,6 @@ pub struct IOCompositor {
}
impl IOCompositor {
-
pub fn new(app: &Application,
opts: Opts,
port: Port<Msg>,
@@ -131,6 +132,7 @@ impl IOCompositor {
opts: opts,
context: rendergl::init_render_context(),
root_layer: root_layer.clone(),
+ root_pipeline: None,
scene: Scene(ContainerLayerKind(root_layer), window_size, identity()),
window_size: Size2D(window_size.width as uint, window_size.height as uint),
graphics_context: CompositorTask::create_graphics_context(),
@@ -253,11 +255,10 @@ impl IOCompositor {
self.change_render_state(render_state);
}
- (Data(SetUnRenderedColor(_id, color)), false) => {
- self.set_unrendered_color(_id, color);
+ (Data(SetUnRenderedColor(pipeline_id, layer_id, color)), false) => {
+ self.set_unrendered_color(pipeline_id, layer_id, color);
}
-
(Data(SetIds(frame_tree, response_chan, new_constellation_chan)), _) => {
self.set_ids(frame_tree, response_chan, new_constellation_chan);
}
@@ -266,32 +267,44 @@ impl IOCompositor {
chan.send(Some(azure_hl::current_graphics_metadata()));
}
- (Data(NewLayer(_id, new_size)), false) => {
- self.create_new_layer(_id, new_size);
+ (Data(CreateRootCompositorLayerIfNecessary(pipeline_id, layer_id, size)),
+ false) => {
+ self.create_root_compositor_layer_if_necessary(pipeline_id, layer_id, size);
}
- (Data(SetLayerPageSize(id, new_size, epoch)), false) => {
- self.set_layer_page_size(id, new_size, epoch);
+ (Data(CreateDescendantCompositorLayerIfNecessary(pipeline_id,
+ layer_id,
+ rect,
+ scroll_behavior)),
+ false) => {
+ self.create_descendant_compositor_layer_if_necessary(pipeline_id,
+ layer_id,
+ rect,
+ scroll_behavior);
}
- (Data(SetLayerClipRect(id, new_rect)), false) => {
- self.set_layer_clip_rect(id, new_rect);
+ (Data(SetLayerPageSize(pipeline_id, layer_id, new_size, epoch)), false) => {
+ self.set_layer_page_size(pipeline_id, layer_id, new_size, epoch);
}
- (Data(DeleteLayer(id)), _) => {
+ (Data(SetLayerClipRect(pipeline_id, layer_id, new_rect)), false) => {
+ self.set_layer_clip_rect(pipeline_id, layer_id, new_rect);
+ }
+
+ (Data(DeleteLayerGroup(id)), _) => {
self.delete_layer(id);
}
- (Data(Paint(id, new_layer_buffer_set, epoch)), false) => {
- self.paint(id, new_layer_buffer_set, epoch);
+ (Data(Paint(pipeline_id, layer_id, new_layer_buffer_set, epoch)), false) => {
+ self.paint(pipeline_id, layer_id, new_layer_buffer_set, epoch);
}
- (Data(InvalidateRect(id, rect)), false) => {
- self.invalidate_rect(id, rect);
+ (Data(InvalidateRect(pipeline_id, layer_id, rect)), false) => {
+ self.invalidate_rect(pipeline_id, layer_id, rect);
}
- (Data(ScrollFragmentPoint(id, point)), false) => {
- self.scroll_fragment_to_point(id, point);
+ (Data(ScrollFragmentPoint(pipeline_id, layer_id, point)), false) => {
+ self.scroll_fragment_to_point(pipeline_id, layer_id, point);
}
(Data(LoadComplete(..)), false) => {
@@ -313,11 +326,10 @@ impl IOCompositor {
}
}
- fn set_unrendered_color(&mut self, _id: PipelineId, color: Color) {
+ // FIXME(#2004, pcwalton): Take the pipeline ID and layer ID into account.
+ fn set_unrendered_color(&mut self, _: PipelineId, _: LayerId, color: Color) {
match self.compositor_layer {
- Some(ref mut layer) => {
- layer.unrendered_color = color;
- }
+ Some(ref mut layer) => layer.unrendered_color = color,
None => {}
}
}
@@ -328,31 +340,7 @@ impl IOCompositor {
new_constellation_chan: ConstellationChan) {
response_chan.send(());
- // This assumes there is at most one child, which should be the case.
- // NOTE: work around borrowchk
- {
- let tmp = self.root_layer.borrow().first_child.borrow();
- match *tmp.get() {
- Some(ref old_layer) => ContainerLayer::remove_child(self.root_layer.clone(),
- old_layer.clone()),
- None => {}
- }
- }
-
- let layer = CompositorLayer::from_frame_tree(frame_tree,
- self.opts.tile_size,
- Some(10000000u),
- self.opts.cpu_painting);
- ContainerLayer::add_child_start(self.root_layer.clone(),
- ContainerLayerKind(layer.root_layer.clone()));
-
- // If there's already a root layer, destroy it cleanly.
- match self.compositor_layer {
- None => {}
- Some(ref mut compositor_layer) => compositor_layer.clear_all(),
- }
-
- self.compositor_layer = Some(layer);
+ self.root_pipeline = Some(frame_tree.pipeline.clone());
// Initialize the new constellation channel by sending it the root window size.
let window_size = self.window.borrow().size();
@@ -366,39 +354,81 @@ impl IOCompositor {
self.constellation_chan = new_constellation_chan;
}
- fn create_new_layer(&mut self, _id: PipelineId, new_size: Size2D<f32>) {
- // FIXME: This should create an additional layer instead of replacing the current one.
- // Once ResizeLayer messages are set up, we can switch to the new functionality.
-
- let p = match self.compositor_layer {
- Some(ref compositor_layer) => compositor_layer.pipeline.clone(),
- None => fail!("Compositor: Received new layer without initialized pipeline"),
+ // FIXME(pcwalton): Take the pipeline ID into account.
+ fn create_root_compositor_layer_if_necessary(&mut self,
+ _: PipelineId,
+ layer_id: LayerId,
+ size: Size2D<f32>) {
+ let (root_pipeline, root_layer_id) = match self.compositor_layer {
+ Some(ref compositor_layer) => {
+ (compositor_layer.pipeline.clone(), compositor_layer.id_of_first_child())
+ }
+ None => {
+ match self.root_pipeline {
+ Some(ref root_pipeline) => (root_pipeline.clone(), LayerId::null()),
+ None => fail!("Compositor: Received new layer without initialized pipeline"),
+ }
+ }
};
- let page_size = Size2D(new_size.width as f32, new_size.height as f32);
- let new_layer = CompositorLayer::new(p,
- Some(page_size),
- self.opts.tile_size,
- Some(10000000u),
- self.opts.cpu_painting);
- {
- let current_child = self.root_layer.borrow().first_child.borrow();
- // This assumes there is at most one child, which should be the case.
- match *current_child.get() {
- Some(ref old_layer) => ContainerLayer::remove_child(self.root_layer.clone(),
- old_layer.clone()),
- None => {}
+ if layer_id != root_layer_id {
+ let root_pipeline_id = root_pipeline.id;
+ let mut new_layer = CompositorLayer::new_root(root_pipeline,
+ size,
+ self.opts.tile_size,
+ self.opts.cpu_painting);
+
+ {
+ let current_child = self.root_layer.borrow().first_child.borrow();
+ match *current_child.get() {
+ None => {}
+ Some(ref old_layer) => {
+ ContainerLayer::remove_child(self.root_layer.clone(), old_layer.clone())
+ }
+ }
}
+
+ assert!(new_layer.add_child_if_necessary(self.root_layer.clone(),
+ root_pipeline_id,
+ new_layer.id,
+ layer_id,
+ Rect(Point2D(0f32, 0f32), size),
+ size,
+ Scrollable));
+
+ ContainerLayer::add_child_start(self.root_layer.clone(),
+ ContainerLayerKind(new_layer.root_layer.clone()));
+ self.compositor_layer = Some(new_layer);
}
- ContainerLayer::add_child_start(self.root_layer.clone(),
- ContainerLayerKind(new_layer.root_layer.clone()));
- self.compositor_layer = Some(new_layer);
+
+ self.ask_for_tiles();
+ }
+
+ fn create_descendant_compositor_layer_if_necessary(&mut self,
+ pipeline_id: PipelineId,
+ layer_id: LayerId,
+ rect: Rect<f32>,
+ scroll_policy: ScrollPolicy) {
+ match self.compositor_layer {
+ Some(ref mut compositor_layer) => {
+ assert!(compositor_layer.add_child_if_necessary(self.root_layer.clone(),
+ pipeline_id,
+ compositor_layer.id,
+ layer_id,
+ rect,
+ compositor_layer.page_size
+ .unwrap(),
+ scroll_policy))
+ }
+ None => fail!("Compositor: Received new layer without initialized pipeline"),
+ };
self.ask_for_tiles();
}
fn set_layer_page_size(&mut self,
- id: PipelineId,
+ pipeline_id: PipelineId,
+ layer_id: LayerId,
new_size: Size2D<f32>,
epoch: Epoch) {
let (ask, move): (bool, bool) = match self.compositor_layer {
@@ -407,8 +437,10 @@ impl IOCompositor {
let world_zoom = self.world_zoom;
let page_window = Size2D(window_size.width as f32 / world_zoom,
window_size.height as f32 / world_zoom);
- layer.resize(id, new_size, page_window, epoch);
- let move = self.fragment_point.take().map_or(false, |point| layer.move(point, page_window));
+ layer.resize(pipeline_id, layer_id, new_size, page_window, epoch);
+ let move = self.fragment_point.take().map_or(false, |point| {
+ layer.move(pipeline_id, layer_id, point, page_window)
+ });
(true, move)
}
@@ -421,10 +453,13 @@ impl IOCompositor {
}
}
- fn set_layer_clip_rect(&mut self, id: PipelineId, new_rect: Rect<f32>) {
+ fn set_layer_clip_rect(&mut self,
+ pipeline_id: PipelineId,
+ layer_id: LayerId,
+ new_rect: Rect<f32>) {
let ask: bool = match self.compositor_layer {
Some(ref mut layer) => {
- assert!(layer.set_clipping_rect(id, new_rect));
+ assert!(layer.set_clipping_rect(pipeline_id, layer_id, new_rect));
true
}
None => {
@@ -454,10 +489,11 @@ impl IOCompositor {
}
fn paint(&mut self,
- id: PipelineId,
+ pipeline_id: PipelineId,
+ layer_id: LayerId,
new_layer_buffer_set: ~LayerBufferSet,
epoch: Epoch) {
- debug!("osmain: received new frame");
+ debug!("compositor received new frame");
// From now on, if we destroy the buffers, they will leak.
let mut new_layer_buffer_set = new_layer_buffer_set;
@@ -466,27 +502,29 @@ impl IOCompositor {
match self.compositor_layer {
Some(ref mut layer) => {
assert!(layer.add_buffers(&self.graphics_context,
- id,
+ pipeline_id,
+ layer_id,
new_layer_buffer_set,
epoch).is_none());
self.recomposite = true;
}
None => {
- fail!("Compositor: given paint command with no CompositorLayer initialized");
+ fail!("compositor given paint command with no CompositorLayer initialized");
}
}
+
// TODO: Recycle the old buffers; send them back to the renderer to reuse if
// it wishes.
}
- fn invalidate_rect(&mut self, id: PipelineId, rect: Rect<uint>) {
+ fn invalidate_rect(&mut self, pipeline_id: PipelineId, layer_id: LayerId, rect: Rect<uint>) {
let ask: bool = match self.compositor_layer {
Some(ref mut layer) => {
let point = Point2D(rect.origin.x as f32,
rect.origin.y as f32);
let size = Size2D(rect.size.width as f32,
rect.size.height as f32);
- layer.invalidate_rect(id, Rect(point, size));
+ layer.invalidate_rect(pipeline_id, layer_id, Rect(point, size));
true
}
None => {
@@ -500,14 +538,18 @@ impl IOCompositor {
}
}
- fn scroll_fragment_to_point(&mut self, id: PipelineId, point: Point2D<f32>) {
+ fn scroll_fragment_to_point(&mut self,
+ pipeline_id: PipelineId,
+ layer_id: LayerId,
+ point: Point2D<f32>) {
let world_zoom = self.world_zoom;
let page_window = Size2D(self.window_size.width as f32 / world_zoom,
self.window_size.height as f32 / world_zoom);
+
let (ask, move): (bool, bool) = match self.compositor_layer {
- Some(ref mut layer) if layer.pipeline.id == id && !layer.hidden => {
+ Some(ref mut layer) if layer.pipeline.id == pipeline_id && !layer.hidden => {
- (true, layer.move(point, page_window))
+ (true, layer.move(pipeline_id, layer_id, point, page_window))
}
Some(_) | None => {
self.fragment_point = Some(point);
@@ -630,7 +672,7 @@ impl IOCompositor {
self.window_size.height as f32 / world_zoom);
let mut scroll = false;
for layer in self.compositor_layer.mut_iter() {
- scroll = layer.scroll(page_delta, page_cursor, page_window) || scroll;
+ scroll = layer.handle_scroll_event(page_delta, page_cursor, page_window) || scroll;
}
self.recomposite_if(scroll);
self.ask_for_tiles();
@@ -656,7 +698,7 @@ impl IOCompositor {
let page_window = Size2D(window_size.width as f32 / world_zoom,
window_size.height as f32 / world_zoom);
for layer in self.compositor_layer.mut_iter() {
- layer.scroll(page_delta, page_cursor, page_window);
+ layer.handle_scroll_event(page_delta, page_cursor, page_window);
}
self.recomposite = true;
@@ -679,7 +721,9 @@ impl IOCompositor {
for layer in self.compositor_layer.mut_iter() {
if !layer.hidden {
let rect = Rect(Point2D(0f32, 0f32), window_size_page);
- let recomposite = layer.get_buffer_request(&self.graphics_context, rect, world_zoom) ||
+ let recomposite = layer.get_buffer_request(&self.graphics_context,
+ rect,
+ world_zoom) ||
self.recomposite;
self.recomposite = recomposite;
} else {
@@ -756,3 +800,4 @@ impl IOCompositor {
self.recomposite = result || self.recomposite;
}
}
+
diff --git a/src/components/main/compositing/compositor_layer.rs b/src/components/main/compositing/compositor_layer.rs
index cfea4af009c..971e7c096d8 100644
--- a/src/components/main/compositing/compositor_layer.rs
+++ b/src/components/main/compositing/compositor_layer.rs
@@ -3,42 +3,56 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use compositing::quadtree::{Quadtree, Normal, Invalid, Hidden};
-use constellation::{SendableChildFrameTree, SendableFrameTree};
+use pipeline::CompositionPipeline;
+use windowing::{MouseWindowEvent, MouseWindowClickEvent, MouseWindowMouseDownEvent};
+use windowing::{MouseWindowMouseUpEvent};
+
+use azure::azure_hl::Color;
use geom::matrix::identity;
use geom::point::Point2D;
use geom::rect::Rect;
use geom::size::Size2D;
use gfx::render_task::{ReRenderMsg, UnusedBufferMsg};
+use gfx;
use layers::layers::{ContainerLayerKind, ContainerLayer, Flip, NoFlip, TextureLayer};
use layers::layers::TextureLayerKind;
-#[cfg(target_os="macos")]
-#[cfg(target_os="android")]
-use layers::layers::VerticalFlip;
use layers::platform::surface::{NativeCompositingGraphicsContext, NativeSurfaceMethods};
use layers::texturegl::{Texture, TextureTarget};
-#[cfg(target_os="macos")] use layers::texturegl::TextureTargetRectangle;
-use pipeline::CompositionPipeline;
use script::dom::event::{ClickEvent, MouseDownEvent, MouseMoveEvent, MouseUpEvent};
use script::script_task::{ScriptChan, SendEventMsg};
-use servo_msg::compositor_msg::{LayerBuffer, LayerBufferSet, Epoch, Tile};
+use servo_msg::compositor_msg::{Epoch, FixedPosition, LayerBuffer, LayerBufferSet, LayerId};
+use servo_msg::compositor_msg::{ScrollPolicy, Tile};
use servo_msg::constellation_msg::PipelineId;
use std::cmp;
use std::rc::Rc;
-use windowing::{MouseWindowEvent, MouseWindowClickEvent, MouseWindowMouseDownEvent};
-use windowing::{MouseWindowMouseUpEvent};
-use azure::azure_hl::Color;
-use gfx;
+#[cfg(target_os="macos")]
+#[cfg(target_os="android")]
+use layers::layers::VerticalFlip;
#[cfg(not(target_os="macos"))]
use layers::texturegl::TextureTarget2D;
+#[cfg(target_os="macos")]
+use layers::texturegl::TextureTargetRectangle;
+
+/// The amount of memory usage allowed per layer.
+static MAX_TILE_MEMORY_PER_LAYER: uint = 10000000;
/// The CompositorLayer represents an element on a page that has a unique scroll
/// or animation behavior. This can include absolute positioned elements, iframes, etc.
/// Each layer can also have child layers.
+///
+/// FIXME(#2003, pcwalton): This should be merged with the concept of a layer in `rust-layers` and
+/// ultimately removed, except as a set of helper methods on `rust-layers` layers.
pub struct CompositorLayer {
/// This layer's pipeline. BufferRequests and mouse events will be sent through this.
pipeline: CompositionPipeline,
+ /// The ID of this layer within the pipeline.
+ id: LayerId,
+
+ /// The bounds of this layer in terms of its parent (a.k.a. the scissor box).
+ bounds: Rect<f32>,
+
/// The size of the underlying page in page coordinates. This is an option
/// because we may not know the size of the page until layout is finished completely.
/// if we have no size yet, the layer is hidden until a size message is recieved.
@@ -70,17 +84,20 @@ pub struct CompositorLayer {
epoch: Epoch,
/// The behavior of this layer when a scroll message is received.
- scroll_behavior: ScrollBehavior,
+ wants_scroll_events: WantsScrollEventsFlag,
+
+ /// Whether an ancestor layer that receives scroll events moves this layer.
+ scroll_policy: ScrollPolicy,
/// True if CPU rendering is enabled, false if we're using GPU rendering.
cpu_painting: bool,
/// The color to use for the unrendered-content void
- unrendered_color: Color
+ unrendered_color: Color,
}
/// Helper struct for keeping CompositorLayer children organized.
-struct CompositorLayerChild {
+pub struct CompositorLayerChild {
/// The child itself.
child: ~CompositorLayer,
/// A ContainerLayer managed by the parent node. This deals with clipping and
@@ -95,13 +112,28 @@ enum MaybeQuadtree {
NoTree(uint, Option<uint>),
}
-/// Determines the behavior of the layer when a scroll message is recieved.
-enum ScrollBehavior {
- /// Normal scrolling behavior.
- Scroll,
- /// Scrolling messages targeted at this layer are ignored, but can be
- /// passed on to child layers.
- FixedPosition,
+impl MaybeQuadtree {
+ fn tile_size(&self) -> uint {
+ match *self {
+ Tree(ref quadtree) => quadtree.max_tile_size,
+ NoTree(tile_size, _) => tile_size,
+ }
+ }
+}
+
+#[deriving(Eq, Clone)]
+pub enum WantsScrollEventsFlag {
+ WantsScrollEvents,
+ DoesntWantScrollEvents,
+}
+
+fn create_container_layer_from_rect(rect: Rect<f32>) -> Rc<ContainerLayer> {
+ let container = Rc::new(ContainerLayer());
+ container.borrow().scissor.set(Some(rect));
+ container.borrow().common.with_mut(|common| {
+ common.transform = identity().translate(rect.origin.x, rect.origin.y, 0f32);
+ });
+ container
}
trait Clampable {
@@ -124,79 +156,155 @@ impl Clampable for f32 {
impl CompositorLayer {
- /// Creates a new CompositorLayer with an optional page size. If no page size is given,
- /// the layer is initially hidden and initialized without a quadtree.
- pub fn new(pipeline: CompositionPipeline,
- page_size: Option<Size2D<f32>>,
- tile_size: uint,
- max_mem: Option<uint>,
- cpu_painting: bool)
- -> CompositorLayer {
+ /// Creates a new `CompositorLayer`.
+ fn new(pipeline: CompositionPipeline,
+ layer_id: LayerId,
+ bounds: Rect<f32>,
+ page_size: Option<Size2D<f32>>,
+ tile_size: uint,
+ cpu_painting: bool,
+ wants_scroll_events: WantsScrollEventsFlag,
+ scroll_policy: ScrollPolicy)
+ -> CompositorLayer {
CompositorLayer {
pipeline: pipeline,
+ id: layer_id,
+ bounds: bounds,
page_size: page_size,
scroll_offset: Point2D(0f32, 0f32),
children: ~[],
quadtree: match page_size {
- None => NoTree(tile_size, max_mem),
- Some(page_size) => Tree(Quadtree::new(Size2D(page_size.width as uint,
- page_size.height as uint),
- tile_size,
- max_mem)),
+ None => NoTree(tile_size, Some(MAX_TILE_MEMORY_PER_LAYER)),
+ Some(page_size) => {
+ Tree(Quadtree::new(Size2D(page_size.width as uint, page_size.height as uint),
+ tile_size,
+ Some(MAX_TILE_MEMORY_PER_LAYER)))
+ }
},
root_layer: Rc::new(ContainerLayer()),
hidden: true,
epoch: Epoch(0),
- scroll_behavior: Scroll,
+ wants_scroll_events: wants_scroll_events,
+ scroll_policy: scroll_policy,
cpu_painting: cpu_painting,
unrendered_color: gfx::color::rgba(0.0, 0.0, 0.0, 0.0),
}
}
- /// Constructs a CompositorLayer tree from a frame tree.
- pub fn from_frame_tree(frame_tree: SendableFrameTree,
- tile_size: uint,
- max_mem: Option<uint>,
- cpu_painting: bool)
- -> CompositorLayer {
- let SendableFrameTree { pipeline, children } = frame_tree;
- let mut layer = CompositorLayer::new(pipeline, None, tile_size, max_mem, cpu_painting);
- layer.children = (children.move_iter().map(|child| {
- let SendableChildFrameTree { frame_tree, rect } = child;
- let container = Rc::new(ContainerLayer());
- match rect {
- Some(rect) => {
- container.borrow().scissor.set(Some(rect));
- container.borrow().common.with_mut(|common| common.transform = identity().translate(rect.origin.x,
- rect.origin.y,
- 0f32));
- }
- None => {}
- }
+ /// Creates a new root `CompositorLayer` bound to a composition pipeline with an optional page
+ /// size. If no page size is given, the layer is initially hidden and initialized without a
+ /// quadtree.
+ pub fn new_root(pipeline: CompositionPipeline,
+ page_size: Size2D<f32>,
+ tile_size: uint,
+ cpu_painting: bool)
+ -> CompositorLayer {
+ CompositorLayer {
+ pipeline: pipeline,
+ id: LayerId::null(),
+ bounds: Rect(Point2D(0f32, 0f32), page_size),
+ page_size: Some(page_size),
+ scroll_offset: Point2D(0f32, 0f32),
+ children: ~[],
+ quadtree: NoTree(tile_size, Some(MAX_TILE_MEMORY_PER_LAYER)),
+ root_layer: Rc::new(ContainerLayer()),
+ hidden: false,
+ epoch: Epoch(0),
+ wants_scroll_events: WantsScrollEvents,
+ scroll_policy: FixedPosition,
+ cpu_painting: cpu_painting,
+ unrendered_color: gfx::color::rgba(0.0, 0.0, 0.0, 0.0),
+ }
+ }
- let child_layer = ~CompositorLayer::from_frame_tree(frame_tree,
- tile_size,
- max_mem,
- cpu_painting);
- ContainerLayer::add_child_start(container.clone(), ContainerLayerKind(child_layer.root_layer.clone()));
+ /// Adds a child layer to the layer with the given ID and the given pipeline, if it doesn't
+ /// exist yet. The child layer will have the same pipeline, tile size, memory limit, and CPU
+ /// painting status as its parent.
+ ///
+ /// Returns:
+ /// * True if the layer was added;
+ /// * True if the layer was not added because it already existed;
+ /// * False if the layer could not be added because no suitable parent layer with the given
+ /// ID and pipeline could be found.
+ pub fn add_child_if_necessary(&mut self,
+ container_layer: Rc<ContainerLayer>,
+ pipeline_id: PipelineId,
+ parent_layer_id: LayerId,
+ child_layer_id: LayerId,
+ rect: Rect<f32>,
+ page_size: Size2D<f32>,
+ scroll_policy: ScrollPolicy)
+ -> bool {
+ if self.pipeline.id != pipeline_id || self.id != parent_layer_id {
+ return self.children.mut_iter().any(|kid_holder| {
+ kid_holder.child.add_child_if_necessary(kid_holder.container.clone(),
+ pipeline_id,
+ parent_layer_id,
+ child_layer_id,
+ rect,
+ page_size,
+ scroll_policy)
+ })
+ }
- CompositorLayerChild {
- child: child_layer,
- container: container,
- }
- })).collect();
- layer.set_occlusions();
- layer
+ // See if we've already made this child layer.
+ if self.children.iter().any(|kid_holder| {
+ kid_holder.child.pipeline.id == pipeline_id &&
+ kid_holder.child.id == child_layer_id
+ }) {
+ return true
+ }
+
+ let mut kid = ~CompositorLayer::new(self.pipeline.clone(),
+ child_layer_id,
+ rect,
+ Some(page_size),
+ self.quadtree.tile_size(),
+ self.cpu_painting,
+ DoesntWantScrollEvents,
+ scroll_policy);
+
+ kid.hidden = false;
+
+ // Place the kid's layer in a container...
+ let kid_container = create_container_layer_from_rect(rect);
+ ContainerLayer::add_child_start(kid_container.clone(),
+ ContainerLayerKind(kid.root_layer.clone()));
+
+ // ...and add *that* container as a child of the container passed in.
+ ContainerLayer::add_child_end(container_layer,
+ ContainerLayerKind(kid_container.clone()));
+
+ self.children.push(CompositorLayerChild {
+ child: kid,
+ container: kid_container,
+ });
+ true
}
- // Move the layer by as relative specified amount in page coordinates. Does not change
- // the position of the layer relative to its parent. This also takes in a cursor position
- // to see if the mouse is over child layers first. If a layer successfully scrolled, returns
- // true; otherwise returns false, so a parent layer can scroll instead.
- pub fn scroll(&mut self, delta: Point2D<f32>, cursor: Point2D<f32>, window_size: Size2D<f32>)
- -> bool {
+ /// Move the layer's descendants that don't want scroll events and scroll by a relative
+ /// specified amount in page coordinates. This also takes in a cursor position to see if the
+ /// mouse is over child layers first. If a layer successfully scrolled, returns true; otherwise
+ /// returns false, so a parent layer can scroll instead.
+ pub fn handle_scroll_event(&mut self,
+ delta: Point2D<f32>,
+ cursor: Point2D<f32>,
+ window_size: Size2D<f32>)
+ -> bool {
+ // If this layer is hidden, neither it nor its children will scroll.
+ if self.hidden {
+ return false
+ }
+
+ // If this layer doesn't want scroll events, neither it nor its children can handle scroll
+ // events.
+ if self.wants_scroll_events != WantsScrollEvents {
+ return false
+ }
+
+ // Allow children to scroll.
let cursor = cursor - self.scroll_offset;
- for child in self.children.mut_iter().filter(|x| !x.child.hidden) {
+ for child in self.children.mut_iter() {
// NOTE: work around borrowchk
let tmp = child.container.borrow().scissor.borrow();
match *tmp.get() {
@@ -206,44 +314,84 @@ impl CompositorLayer {
Some(rect) => {
if cursor.x >= rect.origin.x && cursor.x < rect.origin.x + rect.size.width
&& cursor.y >= rect.origin.y && cursor.y < rect.origin.y + rect.size.height
- && child.child.scroll(delta, cursor - rect.origin, rect.size) {
- return true;
+ && child.child.handle_scroll_event(delta,
+ cursor - rect.origin,
+ rect.size) {
+ return true
}
}
}
}
// This scroll event is mine!
- match self.scroll_behavior {
- Scroll => {
- // Scroll this layer!
- let old_origin = self.scroll_offset;
- self.scroll_offset = self.scroll_offset + delta;
-
- // bounds checking
- let page_size = match self.page_size {
- Some(size) => size,
- None => fail!("CompositorLayer: tried to scroll with no page size set"),
- };
- let min_x = cmp::min(window_size.width - page_size.width, 0.0);
- self.scroll_offset.x = self.scroll_offset.x.clamp(&min_x, &0.0);
- let min_y = cmp::min(window_size.height - page_size.height, 0.0);
- self.scroll_offset.y = self.scroll_offset.y.clamp(&min_y, &0.0);
-
- // check to see if we scrolled
- if old_origin - self.scroll_offset == Point2D(0f32, 0f32) {
- return false;
- }
+ // Scroll this layer!
+ let old_origin = self.scroll_offset;
+ self.scroll_offset = self.scroll_offset + delta;
+
+ // bounds checking
+ let page_size = match self.page_size {
+ Some(size) => size,
+ None => fail!("CompositorLayer: tried to scroll with no page size set"),
+ };
+ let min_x = cmp::min(window_size.width - page_size.width, 0.0);
+ self.scroll_offset.x = self.scroll_offset.x.clamp(&min_x, &0.0);
+ let min_y = cmp::min(window_size.height - page_size.height, 0.0);
+ self.scroll_offset.y = self.scroll_offset.y.clamp(&min_y, &0.0);
- self.root_layer.borrow().common.with_mut(|common| common.set_transform(identity().translate(self.scroll_offset.x,
- self.scroll_offset.y,
- 0.0)));
- true
+ if old_origin - self.scroll_offset == Point2D(0f32, 0f32) {
+ return false
+ }
+
+ self.scroll(self.scroll_offset)
+ }
+
+ #[allow(dead_code)]
+ fn dump_layer_tree(&self, layer: Rc<ContainerLayer>, indent: ~str) {
+ {
+ let scissor = layer.borrow().scissor.borrow();
+ println!("{}scissor {:?}", indent, *scissor.get());
+ }
+ for kid in layer.borrow().children() {
+ match kid {
+ ContainerLayerKind(ref container_layer) => {
+ self.dump_layer_tree((*container_layer).clone(), indent + " ");
+ }
+ TextureLayerKind(_) => {
+ println!("{} (texture layer)", indent);
+ }
}
- FixedPosition => false, // Ignore this scroll event.
}
}
+ /// Actually scrolls the descendants of a layer that scroll. This is called by
+ /// `handle_scroll_event` above when it determines that a layer wants to scroll.
+ fn scroll(&mut self, scroll_offset: Point2D<f32>) -> bool {
+ let mut result = false;
+
+ // Only scroll this layer if it's not fixed-positioned.
+ if self.scroll_policy != FixedPosition {
+ // Scroll this layer!
+ self.scroll_offset = scroll_offset;
+
+ self.root_layer
+ .borrow()
+ .common
+ .with_mut(|common| {
+ common.set_transform(identity().translate(self.scroll_offset.x,
+ self.scroll_offset.y,
+ 0.0))
+ });
+
+ result = true
+ }
+
+ for kid_holder in self.children.mut_iter() {
+ result = kid_holder.child.scroll(scroll_offset) || result;
+ }
+
+ result
+ }
+
// Takes in a MouseWindowEvent, determines if it should be passed to children, and
// sends the event off to the appropriate pipeline. NB: the cursor position is in
// page coordinates.
@@ -282,43 +430,49 @@ impl CompositorLayer {
chan.try_send(SendEventMsg(self.pipeline.id.clone(), message));
}
- // Given the current window size, determine which tiles need to be (re)rendered
- // and sends them off the the appropriate renderer.
- // Returns a bool that is true if the scene should be repainted.
+ // Given the current window size, determine which tiles need to be (re-)rendered and sends them
+ // off the the appropriate renderer. Returns true if and only if the scene should be repainted.
pub fn get_buffer_request(&mut self,
graphics_context: &NativeCompositingGraphicsContext,
window_rect: Rect<f32>,
scale: f32)
-> bool {
- let rect = Rect(Point2D(-self.scroll_offset.x + window_rect.origin.x,
- -self.scroll_offset.y + window_rect.origin.y),
- window_rect.size);
- let mut redisplay: bool;
- { // block here to prevent double mutable borrow of self
- let quadtree = match self.quadtree {
- NoTree(..) => fail!("CompositorLayer: cannot get buffer request for {:?},
- no quadtree initialized", self.pipeline.id),
- Tree(ref mut quadtree) => quadtree,
- };
- let (request, unused) = quadtree.get_tile_rects_page(rect, scale);
- redisplay = !unused.is_empty(); // workaround to make redisplay visible outside block
- if redisplay { // send back unused tiles
- self.pipeline.render_chan.try_send(UnusedBufferMsg(unused));
- }
- if !request.is_empty() { // ask for tiles
- self.pipeline.render_chan.try_send(ReRenderMsg(request, scale, self.epoch));
+ let mut redisplay = false;
+ match self.quadtree {
+ NoTree(..) => {}
+ Tree(ref mut quadtree) => {
+ let (request, unused) = quadtree.get_tile_rects_page(window_rect, scale);
+
+ // Workaround to make redisplay visible outside block.
+ redisplay = !unused.is_empty();
+ if redisplay {
+ // Send back unused tiles.
+ self.pipeline.render_chan.try_send(UnusedBufferMsg(unused));
+ }
+ if !request.is_empty() {
+ // Ask for tiles.
+ //
+ // FIXME(#2003, pcwalton): We may want to batch these up in the case in which
+ // one page has multiple layers, to avoid the user seeing inconsistent states.
+ let msg = ReRenderMsg(request, scale, self.id, self.epoch);
+ self.pipeline.render_chan.try_send(msg);
+ }
}
- }
+ };
+
if redisplay {
self.build_layer_tree(graphics_context);
}
+
let transform = |x: &mut CompositorLayerChild| -> bool {
// NOTE: work around borrowchk
let tmp = x.container.borrow().scissor.borrow();
match *tmp.get() {
Some(scissor) => {
- let new_rect = rect.intersection(&scissor);
- match new_rect {
+ let mut new_rect = window_rect;
+ new_rect.origin.x = new_rect.origin.x - x.child.scroll_offset.x;
+ new_rect.origin.y = new_rect.origin.y - x.child.scroll_offset.y;
+ match new_rect.intersection(&scissor) {
Some(new_rect) => {
// Child layers act as if they are rendered at (0,0), so we
// subtract the layer's (x,y) coords in its containing page
@@ -328,13 +482,11 @@ impl CompositorLayer {
x.child.get_buffer_request(graphics_context, child_rect, scale)
}
None => {
- false //Layer is offscreen
+ false // Layer is offscreen
}
}
}
- None => {
- fail!("CompositorLayer: Child layer not clipped");
- }
+ None => fail!("child layer not clipped!"),
}
};
self.children.mut_iter().filter(|x| !x.child.hidden)
@@ -347,9 +499,18 @@ impl CompositorLayer {
// and clip the layer to the specified size in page coordinates.
// If the layer is hidden and has a defined page size, unhide it.
// This method returns false if the specified layer is not found.
- pub fn set_clipping_rect(&mut self, pipeline_id: PipelineId, new_rect: Rect<f32>) -> bool {
- match self.children.iter().position(|x| pipeline_id == x.child.pipeline.id) {
+ pub fn set_clipping_rect(&mut self,
+ pipeline_id: PipelineId,
+ layer_id: LayerId,
+ new_rect: Rect<f32>)
+ -> bool {
+ debug!("compositor_layer: starting set_clipping_rect()");
+ match self.children.iter().position(|kid_holder| {
+ pipeline_id == kid_holder.child.pipeline.id &&
+ layer_id == kid_holder.child.id
+ }) {
Some(i) => {
+ debug!("compositor_layer: node found for set_clipping_rect()");
let child_node = &mut self.children[i];
child_node.container.borrow().common.with_mut(|common|
common.set_transform(identity().translate(new_rect.origin.x,
@@ -360,6 +521,7 @@ impl CompositorLayer {
let tmp = child_node.container.borrow().scissor.borrow();
tmp.get().clone()
};
+
child_node.container.borrow().scissor.set(Some(new_rect));
match self.quadtree {
NoTree(..) => {} // Nothing to do
@@ -382,72 +544,91 @@ impl CompositorLayer {
None => {
// ID does not match any of our immediate children, so recurse on
// descendents (including hidden children)
- self.children.mut_iter().map(|x| &mut x.child).any(|x| x.set_clipping_rect(pipeline_id, new_rect))
+ self.children
+ .mut_iter()
+ .map(|kid_holder| &mut kid_holder.child)
+ .any(|kid| kid.set_clipping_rect(pipeline_id, layer_id, new_rect))
}
}
}
-
// Set the layer's page size. This signals that the renderer is ready for BufferRequests.
// If the layer is hidden and has a defined clipping rect, unhide it.
// This method returns false if the specified layer is not found.
- pub fn resize(&mut self, pipeline_id: PipelineId, new_size: Size2D<f32>, window_size: Size2D<f32>, epoch: Epoch) -> bool {
- if self.pipeline.id == pipeline_id {
- self.epoch = epoch;
- self.page_size = Some(new_size);
- match self.quadtree {
- Tree(ref mut quadtree) => {
- self.pipeline.render_chan.try_send(UnusedBufferMsg(quadtree.resize(new_size.width as uint,
- new_size.height as uint)));
- }
- NoTree(tile_size, max_mem) => {
- self.quadtree = Tree(Quadtree::new(Size2D(new_size.width as uint,
- new_size.height as uint),
- tile_size,
- max_mem))
- }
+ pub fn resize(&mut self,
+ pipeline_id: PipelineId,
+ layer_id: LayerId,
+ new_size: Size2D<f32>,
+ window_size: Size2D<f32>,
+ epoch: Epoch)
+ -> bool {
+ debug!("compositor_layer: starting resize()");
+ if self.pipeline.id != pipeline_id || self.id != layer_id {
+ return self.resize_helper(pipeline_id, layer_id, new_size, epoch)
+ }
+
+ debug!("compositor_layer: layer found for resize()");
+ self.epoch = epoch;
+ self.page_size = Some(new_size);
+ match self.quadtree {
+ Tree(ref mut quadtree) => {
+ self.pipeline
+ .render_chan
+ .try_send(UnusedBufferMsg(quadtree.resize(new_size.width as uint,
+ new_size.height as uint)));
+ }
+ NoTree(tile_size, max_mem) => {
+ self.quadtree = Tree(Quadtree::new(Size2D(new_size.width as uint,
+ new_size.height as uint),
+ tile_size,
+ max_mem))
}
- // Call scroll for bounds checking if the page shrunk. Use (-1, -1) as the cursor position
- // to make sure the scroll isn't propagated downwards.
- self.scroll(Point2D(0f32, 0f32), Point2D(-1f32, -1f32), window_size);
- self.hidden = false;
- self.set_occlusions();
- true
- } else {
- self.resize_helper(pipeline_id, new_size, epoch)
}
+ // Call scroll for bounds checking if the page shrunk. Use (-1, -1) as the cursor position
+ // to make sure the scroll isn't propagated downwards.
+ self.handle_scroll_event(Point2D(0f32, 0f32), Point2D(-1f32, -1f32), window_size);
+ self.hidden = false;
+ self.set_occlusions();
+ true
}
- pub fn move(&mut self, origin: Point2D<f32>, window_size: Size2D<f32>) -> bool {
- match self.scroll_behavior {
- Scroll => {
- // Scroll this layer!
- let old_origin = self.scroll_offset;
- self.scroll_offset = Point2D(0f32, 0f32) - origin;
-
- // bounds checking
- let page_size = match self.page_size {
- Some(size) => size,
- None => fail!("CompositorLayer: tried to scroll with no page size set"),
- };
- let min_x = cmp::min(window_size.width - page_size.width, 0.0);
- self.scroll_offset.x = self.scroll_offset.x.clamp(&min_x, &0.0);
- let min_y = cmp::min(window_size.height - page_size.height, 0.0);
- self.scroll_offset.y = self.scroll_offset.y.clamp(&min_y, &0.0);
-
- // check to see if we scrolled
- if old_origin - self.scroll_offset == Point2D(0f32, 0f32) {
- return false;
- }
+ pub fn move(&mut self,
+ pipeline_id: PipelineId,
+ layer_id: LayerId,
+ origin: Point2D<f32>,
+ window_size: Size2D<f32>)
+ -> bool {
+ // Search children for the right layer to move.
+ if self.pipeline.id != pipeline_id || self.id != layer_id {
+ return self.children.mut_iter().any(|kid_holder| {
+ kid_holder.child.move(pipeline_id, layer_id, origin, window_size)
+ })
+ }
- self.root_layer.borrow().common.with_mut(|common|
- common.set_transform(identity().translate(self.scroll_offset.x,
- self.scroll_offset.y,
- 0.0)));
- true
- }
- FixedPosition => false // Ignore this scroll event.
+ if self.wants_scroll_events != WantsScrollEvents {
+ return false
+ }
+
+ // Scroll this layer!
+ let old_origin = self.scroll_offset;
+ self.scroll_offset = Point2D(0f32, 0f32) - origin;
+
+ // bounds checking
+ let page_size = match self.page_size {
+ Some(size) => size,
+ None => fail!("CompositorLayer: tried to scroll with no page size set"),
+ };
+ let min_x = cmp::min(window_size.width - page_size.width, 0.0);
+ self.scroll_offset.x = self.scroll_offset.x.clamp(&min_x, &0.0);
+ let min_y = cmp::min(window_size.height - page_size.height, 0.0);
+ self.scroll_offset.y = self.scroll_offset.y.clamp(&min_y, &0.0);
+
+ // check to see if we scrolled
+ if old_origin - self.scroll_offset == Point2D(0f32, 0f32) {
+ return false;
}
+
+ self.scroll(self.scroll_offset)
}
// Returns whether the layer should be vertically flipped.
@@ -479,9 +660,19 @@ impl CompositorLayer {
}
// A helper method to resize sublayers.
- fn resize_helper(&mut self, pipeline_id: PipelineId, new_size: Size2D<f32>, epoch: Epoch) -> bool {
- let found = match self.children.iter().position(|x| pipeline_id == x.child.pipeline.id) {
+ fn resize_helper(&mut self,
+ pipeline_id: PipelineId,
+ layer_id: LayerId,
+ new_size: Size2D<f32>,
+ epoch: Epoch)
+ -> bool {
+ debug!("compositor_layer: starting resize_helper()");
+ let found = match self.children.iter().position(|kid_holder| {
+ pipeline_id == kid_holder.child.pipeline.id &&
+ layer_id == kid_holder.child.id
+ }) {
Some(i) => {
+ debug!("compositor_layer: layer found for resize_helper()");
let child_node = &mut self.children[i];
let child = &mut child_node.child;
child.epoch = epoch;
@@ -502,9 +693,11 @@ impl CompositorLayer {
let tmp = child_node.container.borrow().scissor.borrow();
match *tmp.get() {
Some(scissor) => {
- // Call scroll for bounds checking if the page shrunk. Use (-1, -1) as the cursor position
- // to make sure the scroll isn't propagated downwards.
- child.scroll(Point2D(0f32, 0f32), Point2D(-1f32, -1f32), scissor.size);
+ // Call scroll for bounds checking if the page shrunk. Use (-1, -1) as the
+ // cursor position to make sure the scroll isn't propagated downwards.
+ child.handle_scroll_event(Point2D(0f32, 0f32),
+ Point2D(-1f32, -1f32),
+ scissor.size);
child.hidden = false;
}
None => {} // Nothing to do
@@ -516,11 +709,15 @@ impl CompositorLayer {
if found { // Boolean flag to get around double borrow of self
self.set_occlusions();
- true
- } else {
- // ID does not match ours, so recurse on descendents (including hidden children)
- self.children.mut_iter().map(|x| &mut x.child).any(|x| x.resize_helper(pipeline_id, new_size, epoch))
+ return true
}
+
+ // If we got here, the layer's ID does not match ours, so recurse on descendents (including
+ // hidden children).
+ self.children
+ .mut_iter()
+ .map(|kid_holder| &mut kid_holder.child)
+ .any(|kid_holder| kid_holder.resize_helper(pipeline_id, layer_id, new_size, epoch))
}
// Collect buffers from the quadtree. This method IS NOT recursive, so child CompositorLayers
@@ -577,7 +774,9 @@ impl CompositorLayer {
buffer.native_surface.bind_to_texture(graphics_context, &texture, size);
// Make a texture layer and add it.
- texture_layer = Rc::new(TextureLayer::new(texture, buffer.screen_pos.size, flip));
+ texture_layer = Rc::new(TextureLayer::new(texture,
+ buffer.screen_pos.size,
+ flip));
ContainerLayer::add_child_end(self.root_layer.clone(),
TextureLayerKind(texture_layer.clone()));
None
@@ -632,57 +831,65 @@ impl CompositorLayer {
pub fn add_buffers(&mut self,
graphics_context: &NativeCompositingGraphicsContext,
pipeline_id: PipelineId,
+ layer_id: LayerId,
mut new_buffers: ~LayerBufferSet,
epoch: Epoch)
-> Option<~LayerBufferSet> {
- if self.pipeline.id == pipeline_id {
- if self.epoch != epoch {
- debug!("compositor epoch mismatch: {:?} != {:?}, id: {:?}",
- self.epoch,
- epoch,
- self.pipeline.id);
- self.pipeline.render_chan.try_send(UnusedBufferMsg(new_buffers.buffers));
- return None;
- }
-
- {
- // Block here to prevent double mutable borrow of self.
- let quadtree = match self.quadtree {
- NoTree(..) => fail!("CompositorLayer: cannot add buffers, no quadtree initialized"),
- Tree(ref mut quadtree) => quadtree,
- };
-
- let mut unused_tiles = ~[];
- // move_rev_iter is more efficient
- for buffer in new_buffers.buffers.move_rev_iter() {
- unused_tiles.push_all_move(quadtree.add_tile_pixel(buffer.screen_pos.origin.x,
- buffer.screen_pos.origin.y,
- buffer.resolution, buffer));
- }
- if !unused_tiles.is_empty() { // send back unused buffers
- self.pipeline.render_chan.try_send(UnusedBufferMsg(unused_tiles));
+ debug!("compositor_layer: starting add_buffers()");
+ if self.pipeline.id != pipeline_id || self.id != layer_id {
+ // ID does not match ours, so recurse on descendents (including hidden children).
+ for child_layer in self.children.mut_iter() {
+ match child_layer.child.add_buffers(graphics_context,
+ pipeline_id,
+ layer_id,
+ new_buffers,
+ epoch) {
+ None => return None,
+ Some(buffers) => new_buffers = buffers,
}
}
- self.build_layer_tree(graphics_context);
- return None;
+
+ // Not found. Give the caller the buffers back.
+ return Some(new_buffers)
}
- // ID does not match ours, so recurse on descendents (including hidden children).
- for child_layer in self.children.mut_iter() {
- match child_layer.child.add_buffers(graphics_context,
- pipeline_id,
- new_buffers,
- epoch) {
- None => return None,
- Some(buffers) => new_buffers = buffers,
+ debug!("compositor_layer: layers found for add_buffers()");
+
+ if self.epoch != epoch {
+ debug!("add_buffers: compositor epoch mismatch: {:?} != {:?}, id: {:?}",
+ self.epoch,
+ epoch,
+ self.pipeline.id);
+ self.pipeline.render_chan.try_send(UnusedBufferMsg(new_buffers.buffers));
+ return None
+ }
+
+ {
+ let quadtree = match self.quadtree {
+ NoTree(..) => {
+ fail!("CompositorLayer: cannot add buffers, no quadtree initialized")
+ }
+ Tree(ref mut quadtree) => quadtree,
+ };
+
+ let mut unused_tiles = ~[];
+ for buffer in new_buffers.buffers.move_rev_iter() {
+ unused_tiles.push_all_move(quadtree.add_tile_pixel(buffer.screen_pos.origin.x,
+ buffer.screen_pos.origin.y,
+ buffer.resolution,
+ buffer));
+ }
+ if !unused_tiles.is_empty() { // send back unused buffers
+ self.pipeline.render_chan.try_send(UnusedBufferMsg(unused_tiles));
}
}
- // Not found. Give the caller the buffers back.
- Some(new_buffers)
+ self.build_layer_tree(graphics_context);
+ return None
}
- // Deletes a specified sublayer, including hidden children. Returns false if the layer is not found.
+ // Deletes a specified sublayer, including hidden children. Returns false if the layer is not
+ // found.
pub fn delete(&mut self,
graphics_context: &NativeCompositingGraphicsContext,
pipeline_id: PipelineId)
@@ -717,8 +924,11 @@ impl CompositorLayer {
}
}
- pub fn invalidate_rect(&mut self, pipeline_id: PipelineId, rect: Rect<f32>) -> bool {
- if self.pipeline.id == pipeline_id {
+ pub fn invalidate_rect(&mut self, pipeline_id: PipelineId, layer_id: LayerId, rect: Rect<f32>)
+ -> bool {
+ debug!("compositor_layer: starting invalidate_rect()");
+ if self.pipeline.id == pipeline_id && layer_id == self.id {
+ debug!("compositor_layer: layer found for invalidate_rect()");
let quadtree = match self.quadtree {
NoTree(..) => return true, // Nothing to do
Tree(ref mut quadtree) => quadtree,
@@ -727,7 +937,10 @@ impl CompositorLayer {
true
} else {
// ID does not match ours, so recurse on descendents (including hidden children).
- self.children.mut_iter().map(|x| &mut x.child).any(|x| x.invalidate_rect(pipeline_id, rect))
+ self.children
+ .mut_iter()
+ .map(|kid_holder| &mut kid_holder.child)
+ .any(|kid| kid.invalidate_rect(pipeline_id, layer_id, rect))
}
}
@@ -773,16 +986,6 @@ impl CompositorLayer {
}
}
- /// Destroys all quadtree tiles of all layers, including child layers, sending the buffers
- /// back to the renderer to be destroyed or reused.
- pub fn clear_all(&mut self) {
- self.clear();
-
- for kid in self.children.mut_iter() {
- kid.child.clear_all()
- }
- }
-
/// Destroys all tiles of all layers, including children, *without* sending them back to the
/// renderer. You must call this only when the render task is destined to be going down;
/// otherwise, you will leak tiles.
@@ -804,5 +1007,9 @@ impl CompositorLayer {
kid.child.forget_all_tiles();
}
}
+
+ pub fn id_of_first_child(&self) -> LayerId {
+ self.children.iter().next().expect("no first child!").child.id
+ }
}
diff --git a/src/components/main/compositing/compositor_task.rs b/src/components/main/compositing/compositor_task.rs
index 6666fcf4456..4e0093fb2c4 100644
--- a/src/components/main/compositing/compositor_task.rs
+++ b/src/components/main/compositing/compositor_task.rs
@@ -13,8 +13,8 @@ use geom::point::Point2D;
use geom::rect::Rect;
use geom::size::Size2D;
use layers::platform::surface::{NativeCompositingGraphicsContext, NativeGraphicsMetadata};
-use servo_msg::compositor_msg::{Epoch, RenderListener, LayerBufferSet, RenderState, ReadyState};
-use servo_msg::compositor_msg::ScriptListener;
+use servo_msg::compositor_msg::{Epoch, LayerBufferSet, LayerId, LayerMetadata, ReadyState};
+use servo_msg::compositor_msg::{RenderListener, RenderState, ScriptListener, ScrollPolicy};
use servo_msg::constellation_msg::{ConstellationChan, PipelineId};
use servo_util::opts::Opts;
use servo_util::time::ProfilerChan;
@@ -45,12 +45,15 @@ impl ScriptListener for CompositorChan {
self.chan.send(msg);
}
- fn invalidate_rect(&self, id: PipelineId, rect: Rect<uint>) {
- self.chan.send(InvalidateRect(id, rect));
+ fn invalidate_rect(&self, pipeline_id: PipelineId, layer_id: LayerId, rect: Rect<uint>) {
+ self.chan.send(InvalidateRect(pipeline_id, layer_id, rect));
}
- fn scroll_fragment_point(&self, id: PipelineId, point: Point2D<f32>) {
- self.chan.send(ScrollFragmentPoint(id, point));
+ fn scroll_fragment_point(&self,
+ pipeline_id: PipelineId,
+ layer_id: LayerId,
+ point: Point2D<f32>) {
+ self.chan.send(ScrollFragmentPoint(pipeline_id, layer_id, point));
}
fn close(&self) {
@@ -72,30 +75,68 @@ impl RenderListener for CompositorChan {
port.recv()
}
- fn paint(&self, id: PipelineId, layer_buffer_set: ~LayerBufferSet, epoch: Epoch) {
- self.chan.send(Paint(id, layer_buffer_set, epoch))
+ fn paint(&self,
+ pipeline_id: PipelineId,
+ layer_id: LayerId,
+ layer_buffer_set: ~LayerBufferSet,
+ epoch: Epoch) {
+ self.chan.send(Paint(pipeline_id, layer_id, layer_buffer_set, epoch))
}
- fn new_layer(&self, id: PipelineId, page_size: Size2D<uint>) {
+ fn create_layer_group_for_pipeline(&self, id: PipelineId, page_size: Size2D<uint>) {
let Size2D { width, height } = page_size;
- self.chan.send(NewLayer(id, Size2D(width as f32, height as f32)))
+ self.chan.send(CreateRootCompositorLayerIfNecessary(id,
+ LayerId::null(),
+ Size2D(width as f32, height as f32)))
}
- fn set_layer_page_size_and_color(&self, id: PipelineId, page_size: Size2D<uint>, epoch: Epoch, color: Color) {
- let Size2D { width, height } = page_size;
- self.chan.send(SetUnRenderedColor(id, color));
- self.chan.send(SetLayerPageSize(id, Size2D(width as f32, height as f32), epoch))
+
+ fn initialize_layers_for_pipeline(&self,
+ pipeline_id: PipelineId,
+ metadata: ~[LayerMetadata],
+ epoch: Epoch) {
+ // FIXME(#2004, pcwalton): This assumes that the first layer determines the page size, and
+ // that all other layers are immediate children of it. This is sufficient to handle
+ // `position: fixed` but will not be sufficient to handle `overflow: scroll` or transforms.
+ let mut first = true;
+ for metadata in metadata.iter() {
+ let origin = Point2D(metadata.position.origin.x as f32,
+ metadata.position.origin.y as f32);
+ let size = Size2D(metadata.position.size.width as f32,
+ metadata.position.size.height as f32);
+ let rect = Rect(origin, size);
+ if first {
+ self.chan.send(CreateRootCompositorLayerIfNecessary(pipeline_id,
+ metadata.id,
+ size));
+ first = false
+ } else {
+ self.chan
+ .send(CreateDescendantCompositorLayerIfNecessary(pipeline_id,
+ metadata.id,
+ rect,
+ metadata.scroll_policy));
+ }
+
+ self.chan.send(SetUnRenderedColor(pipeline_id,
+ metadata.id,
+ metadata.background_color));
+ self.chan.send(SetLayerPageSize(pipeline_id, metadata.id, size, epoch));
+ }
}
- fn set_layer_clip_rect(&self, id: PipelineId, new_rect: Rect<uint>) {
+ fn set_layer_clip_rect(&self,
+ pipeline_id: PipelineId,
+ layer_id: LayerId,
+ new_rect: Rect<uint>) {
let new_rect = Rect(Point2D(new_rect.origin.x as f32,
new_rect.origin.y as f32),
Size2D(new_rect.size.width as f32,
new_rect.size.height as f32));
- self.chan.send(SetLayerClipRect(id, new_rect))
+ self.chan.send(SetLayerClipRect(pipeline_id, layer_id, new_rect))
}
- fn delete_layer(&self, id: PipelineId) {
- self.chan.send(DeleteLayer(id))
+ fn delete_layer_group(&self, id: PipelineId) {
+ self.chan.send(DeleteLayerGroup(id))
}
fn set_render_state(&self, render_state: RenderState) {
@@ -133,29 +174,32 @@ pub enum Msg {
/// The headless compositor returns `None`.
GetGraphicsMetadata(Chan<Option<NativeGraphicsMetadata>>),
- /// Alerts the compositor that there is a new layer to be rendered.
- NewLayer(PipelineId, Size2D<f32>),
- /// Alerts the compositor that the specified layer's page has changed size.
- SetLayerPageSize(PipelineId, Size2D<f32>, Epoch),
+ /// Tells the compositor to create the root layer for a pipeline if necessary (i.e. if no layer
+ /// with that ID exists).
+ CreateRootCompositorLayerIfNecessary(PipelineId, LayerId, Size2D<f32>),
+ /// Tells the compositor to create a descendant layer for a pipeline if necessary (i.e. if no
+ /// layer with that ID exists).
+ CreateDescendantCompositorLayerIfNecessary(PipelineId, LayerId, Rect<f32>, ScrollPolicy),
+ /// Alerts the compositor that the specified layer has changed size.
+ SetLayerPageSize(PipelineId, LayerId, Size2D<f32>, Epoch),
/// Alerts the compositor that the specified layer's clipping rect has changed.
- SetLayerClipRect(PipelineId, Rect<f32>),
- /// Alerts the compositor that the specified layer has been deleted.
- DeleteLayer(PipelineId),
+ SetLayerClipRect(PipelineId, LayerId, Rect<f32>),
+ /// Alerts the compositor that the specified pipeline has been deleted.
+ DeleteLayerGroup(PipelineId),
/// Invalidate a rect for a given layer
- InvalidateRect(PipelineId, Rect<uint>),
+ InvalidateRect(PipelineId, LayerId, Rect<uint>),
/// Scroll a page in a window
- ScrollFragmentPoint(PipelineId, Point2D<f32>),
+ ScrollFragmentPoint(PipelineId, LayerId, Point2D<f32>),
/// Requests that the compositor paint the given layer buffer set for the given page size.
- Paint(PipelineId, ~LayerBufferSet, Epoch),
+ Paint(PipelineId, LayerId, ~LayerBufferSet, Epoch),
/// Alerts the compositor to the current status of page loading.
ChangeReadyState(ReadyState),
/// Alerts the compositor to the current status of rendering.
ChangeRenderState(RenderState),
/// Sets the channel to the current layout and render tasks, along with their id
SetIds(SendableFrameTree, Chan<()>, ConstellationChan),
-
- SetUnRenderedColor(PipelineId, Color),
-
+ /// Sets the color of unrendered content for a layer.
+ SetUnRenderedColor(PipelineId, LayerId, Color),
/// The load of a page for a given URL has completed.
LoadComplete(PipelineId, Url),
}
diff --git a/src/components/main/compositing/headless.rs b/src/components/main/compositing/headless.rs
index 19c07bc639d..7acc2e2200a 100644
--- a/src/components/main/compositing/headless.rs
+++ b/src/components/main/compositing/headless.rs
@@ -77,10 +77,11 @@ impl NullCompositor {
// we'll notice and think about whether it needs a response, like
// SetIds.
- NewLayer(..) | SetLayerPageSize(..) | SetLayerClipRect(..) | DeleteLayer(..) |
- Paint(..) | InvalidateRect(..) | ChangeReadyState(..) | ChangeRenderState(..)|
- ScrollFragmentPoint(..) | SetUnRenderedColor(..) | LoadComplete(..)
- => ()
+ CreateRootCompositorLayerIfNecessary(..) |
+ CreateDescendantCompositorLayerIfNecessary(..) | SetLayerPageSize(..) |
+ SetLayerClipRect(..) | DeleteLayerGroup(..) | Paint(..) | InvalidateRect(..) |
+ ChangeReadyState(..) | ChangeRenderState(..) | ScrollFragmentPoint(..) |
+ SetUnRenderedColor(..) | LoadComplete(..) => ()
}
}
}
diff --git a/src/components/main/compositing/quadtree.rs b/src/components/main/compositing/quadtree.rs
index 6c3cda6e2a4..ac4b1d9232e 100644
--- a/src/components/main/compositing/quadtree.rs
+++ b/src/components/main/compositing/quadtree.rs
@@ -460,9 +460,13 @@ impl<T: Tile> QuadtreeNode<T> {
/// treated as invalid as well.
/// NOTE: this method will sometimes modify the tree by deleting tiles.
/// See the QuadTree function description for more details.
- fn get_tile_rects(&mut self, window: Rect<f32>, clip: Size2D<f32>, scale: f32, tile_size: f32, override: bool) ->
- (~[BufferRequest], ~[T], int) {
-
+ fn get_tile_rects(&mut self,
+ window: Rect<f32>,
+ clip: Size2D<f32>,
+ scale: f32,
+ tile_size: f32,
+ override: bool)
+ -> (~[BufferRequest], ~[T], int) {
let w_x = window.origin.x;
let w_y = window.origin.y;
let w_width = window.size.width;
@@ -470,11 +474,11 @@ impl<T: Tile> QuadtreeNode<T> {
let s_x = self.origin.x;
let s_y = self.origin.y;
let s_size = self.size;
-
+
// if window is outside of visible region, nothing to do
if w_x + w_width < s_x || w_x > s_x + s_size
- || w_y + w_height < s_y || w_y > s_y + s_size
- || w_x >= clip.width || w_y >= clip.height {
+ || w_y + w_height < s_y || w_y > s_y + s_size
+ || w_x >= clip.width || w_y >= clip.height {
return (~[], ~[], 0);
}
diff --git a/src/components/main/constellation.rs b/src/components/main/constellation.rs
index 4cc8aa33ccf..4894f94aafd 100644
--- a/src/components/main/constellation.rs
+++ b/src/components/main/constellation.rs
@@ -14,6 +14,7 @@ use script::script_task::{ResizeMsg, ResizeInactiveMsg, ExitPipelineMsg};
use script::layout_interface;
use script::layout_interface::LayoutChan;
use script::script_task::ScriptChan;
+use servo_msg::compositor_msg::LayerId;
use servo_msg::constellation_msg::{ConstellationChan, ExitMsg, FailureMsg, Failure, FrameRectMsg};
use servo_msg::constellation_msg::{IFrameSandboxState, IFrameUnsandboxed, InitLoadUrlMsg};
use servo_msg::constellation_msg::{LoadCompleteMsg, LoadIframeUrlMsg, LoadUrlMsg, Msg, NavigateMsg};
@@ -98,15 +99,6 @@ pub struct SendableChildFrameTree {
rect: Option<Rect<f32>>,
}
-// impl SendableFrameTree {
-// fn contains(&self, id: PipelineId) -> bool {
-// self.pipeline.id == id ||
-// self.children.iter().any(|&SendableChildFrameTree { frame_tree: ref frame_tree, .. }| {
-// frame_tree.contains(id)
-// })
-// }
-// }
-
enum ReplaceResult {
ReplacedNode(Rc<FrameTree>),
OriginalNode(Rc<FrameTree>),
@@ -511,11 +503,12 @@ impl Constellation {
== subpage_id
};
+ let frames = self.find_all(pipeline_id);
+
{
// Update a child's frame rect and inform its script task of the change,
// if it hasn't been already. Optionally inform the compositor if
// resize happens immediately.
- let compositor_chan = self.compositor_chan.clone();
let update_child_rect = |child_frame_tree: &mut ChildFrameTree, is_active: bool| {
child_frame_tree.rect = Some(rect.clone());
// NOTE: work around borrowchk issues
@@ -524,21 +517,19 @@ impl Constellation {
let Size2D { width, height } = rect.size;
if is_active {
let pipeline = pipeline.get().borrow();
- let ScriptChan(ref chan) = pipeline.script_chan;
- chan.send(ResizeMsg(pipeline.id, Size2D {
+ let ScriptChan(ref script_chan) = pipeline.script_chan;
+ script_chan.send(ResizeMsg(pipeline.id, Size2D {
width: width as uint,
height: height as uint
}));
- compositor_chan.send(SetLayerClipRect(pipeline.id, rect));
+ self.compositor_chan.send(SetLayerClipRect(pipeline.id,
+ LayerId::null(),
+ rect));
} else {
let pipeline = pipeline.get().borrow();
- let ScriptChan(ref chan) = pipeline.script_chan;
- chan.send(ResizeInactiveMsg(pipeline.id,
- Size2D(width as uint, height as uint)));
+ already_sent.insert(pipeline.id);
}
- let pipeline = pipeline.get().borrow();
- already_sent.insert(pipeline.id);
- }
+ };
};
// If the subframe is in the current frame tree, the compositor needs the new size
@@ -554,7 +545,6 @@ impl Constellation {
}
// Update all frames with matching pipeline- and subpage-ids
- let frames = self.find_all(pipeline_id);
for frame_tree in frames.iter() {
// NOTE: work around borrowchk issues
let mut tmp = frame_tree.borrow().children.borrow_mut();
diff --git a/src/components/main/css/node_util.rs b/src/components/main/css/node_util.rs
index d6caaad5f01..3b37d4ce016 100644
--- a/src/components/main/css/node_util.rs
+++ b/src/components/main/css/node_util.rs
@@ -5,7 +5,7 @@
use layout::incremental::RestyleDamage;
use layout::util::LayoutDataAccess;
use layout::wrapper::{TLayoutNode, ThreadSafeLayoutNode};
-
+use layout::wrapper::{After, AfterBlock, Before, BeforeBlock, Normal};
use std::cast;
use style::ComputedValues;
use sync::Arc;
@@ -25,13 +25,35 @@ impl<'ln> NodeUtil for ThreadSafeLayoutNode<'ln> {
fn get_css_select_results<'a>(&'a self) -> &'a Arc<ComputedValues> {
unsafe {
let layout_data_ref = self.borrow_layout_data();
- cast::transmute_region(layout_data_ref.get()
- .as_ref()
- .unwrap()
- .data
- .style
- .as_ref()
- .unwrap())
+ match self.get_element_type() {
+ Before | BeforeBlock => {
+ cast::transmute_region(layout_data_ref.get()
+ .as_ref()
+ .unwrap()
+ .data
+ .before_style
+ .as_ref()
+ .unwrap())
+ }
+ After | AfterBlock => {
+ cast::transmute_region(layout_data_ref.get()
+ .as_ref()
+ .unwrap()
+ .data
+ .after_style
+ .as_ref()
+ .unwrap())
+ }
+ Normal => {
+ cast::transmute_region(layout_data_ref.get()
+ .as_ref()
+ .unwrap()
+ .data
+ .style
+ .as_ref()
+ .unwrap())
+ }
+ }
}
}
diff --git a/src/components/main/layout/block.rs b/src/components/main/layout/block.rs
index 53585c47df7..d8132650c70 100644
--- a/src/components/main/layout/block.rs
+++ b/src/components/main/layout/block.rs
@@ -15,21 +15,30 @@
use layout::box_::{Box, ImageBox, ScannedTextBox};
use layout::construct::FlowConstructor;
use layout::context::LayoutContext;
-use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData};
-use layout::floats::{FloatKind, Floats, PlacementInfo};
+use layout::display_list_builder::{DisplayListBuilder, DisplayListBuildingInfo};
+use layout::floats::{ClearBoth, ClearLeft, ClearRight, FloatKind, Floats, PlacementInfo};
use layout::flow::{BaseFlow, BlockFlowClass, FlowClass, Flow, ImmutableFlowUtils};
-use layout::flow::{mut_base, PreorderFlowTraversal, PostorderFlowTraversal, MutableFlowUtils};
+use layout::flow::{MutableFlowUtils, PreorderFlowTraversal, PostorderFlowTraversal, mut_base};
use layout::flow;
-use layout::model::{MaybeAuto, Specified, Auto, specified_or_none, specified};
+use layout::model::{Auto, IntrinsicWidths, MarginCollapseInfo, MarginsCollapse};
+use layout::model::{MarginsCollapseThrough, MaybeAuto, NoCollapsibleMargins, Specified, specified};
+use layout::model::{specified_or_none};
use layout::wrapper::ThreadSafeLayoutNode;
-use style::computed_values::{position};
+use style::ComputedValues;
+use style::computed_values::{clear, position};
-use std::cell::RefCell;
use geom::{Point2D, Rect, Size2D};
-use gfx::display_list::{DisplayListCollection, DisplayList};
+use gfx::color;
+use gfx::display_list::{BackgroundAndBorderLevel, BlockLevel, RootOfStackingContextLevel};
+use gfx::display_list::{StackingContext};
+use gfx::render_task::RenderLayer;
+use servo_msg::compositor_msg::{FixedPosition, LayerId, Scrollable};
use servo_util::geometry::Au;
use servo_util::geometry;
use servo_util::smallvec::{SmallVec, SmallVec0};
+use style::computed_values::{LPA_Auto, LPA_Length, LPA_Percentage, LPN_Length, LPN_None};
+use style::computed_values::{LPN_Percentage, LP_Length, LP_Percentage};
+use sync::Arc;
/// Information specific to floated blocks.
pub struct FloatedBlockInfo {
@@ -98,7 +107,7 @@ impl HeightConstraintSolution {
content_height: Au,
available_height: Au,
static_y_offset: Au)
- -> HeightConstraintSolution {
+ -> HeightConstraintSolution {
// Distance from the top edge of the Absolute Containing Block to the
// top margin edge of a hypothetical box that would have been the
// first box of the element.
@@ -265,6 +274,115 @@ impl HeightConstraintSolution {
}
}
+/// Performs height calculations potentially multiple times, taking `height`, `min-height`, and
+/// `max-height` into account. After each call to `next()`, the caller must call `.try()` with the
+/// current calculated value of `height`.
+///
+/// See CSS 2.1 § 10.7.
+struct CandidateHeightIterator {
+ height: MaybeAuto,
+ max_height: Option<Au>,
+ min_height: Au,
+ candidate_value: Au,
+ status: CandidateHeightIteratorStatus,
+}
+
+impl CandidateHeightIterator {
+ /// Creates a new candidate height iterator. `block_container_height` is `None` if the height
+ /// of the block container has not been determined yet. It will always be `Some` in the case of
+ /// absolutely-positioned containing blocks.
+ pub fn new(style: &ComputedValues, block_container_height: Option<Au>)
+ -> CandidateHeightIterator {
+ // Per CSS 2.1 § 10.7, percentages in `min-height` and `max-height` refer to the height of
+ // the containing block. If that is not determined yet by the time we need to resolve
+ // `min-height` and `max-height`, percentage values are ignored.
+
+ let height = match (style.Box.get().height, block_container_height) {
+ (LPA_Percentage(percent), Some(block_container_height)) => {
+ Specified(block_container_height.scale_by(percent))
+ }
+ (LPA_Percentage(_), None) | (LPA_Auto, _) => Auto,
+ (LPA_Length(length), _) => Specified(length),
+ };
+ let max_height = match (style.Box.get().max_height, block_container_height) {
+ (LPN_Percentage(percent), Some(block_container_height)) => {
+ Some(block_container_height.scale_by(percent))
+ }
+ (LPN_Percentage(_), None) | (LPN_None, _) => None,
+ (LPN_Length(length), _) => Some(length),
+ };
+ let min_height = match (style.Box.get().min_height, block_container_height) {
+ (LP_Percentage(percent), Some(block_container_height)) => {
+ block_container_height.scale_by(percent)
+ }
+ (LP_Percentage(_), None) => Au(0),
+ (LP_Length(length), _) => length,
+ };
+
+ CandidateHeightIterator {
+ height: height,
+ max_height: max_height,
+ min_height: min_height,
+ candidate_value: Au(0),
+ status: InitialCandidateHeightStatus,
+ }
+ }
+
+ pub fn next<'a>(&'a mut self) -> Option<(MaybeAuto, &'a mut Au)> {
+ self.status = match self.status {
+ InitialCandidateHeightStatus => TryingHeightCandidateHeightStatus,
+ TryingHeightCandidateHeightStatus => {
+ match self.max_height {
+ Some(max_height) if self.candidate_value > max_height => {
+ TryingMaxCandidateHeightStatus
+ }
+ _ if self.candidate_value < self.min_height => TryingMinCandidateHeightStatus,
+ _ => FoundCandidateHeightStatus,
+ }
+ }
+ TryingMaxCandidateHeightStatus => {
+ if self.candidate_value < self.min_height {
+ TryingMinCandidateHeightStatus
+ } else {
+ FoundCandidateHeightStatus
+ }
+ }
+ TryingMinCandidateHeightStatus | FoundCandidateHeightStatus => {
+ FoundCandidateHeightStatus
+ }
+ };
+
+ match self.status {
+ TryingHeightCandidateHeightStatus => Some((self.height, &mut self.candidate_value)),
+ TryingMaxCandidateHeightStatus => {
+ Some((Specified(self.max_height.unwrap()), &mut self.candidate_value))
+ }
+ TryingMinCandidateHeightStatus => {
+ Some((Specified(self.min_height), &mut self.candidate_value))
+ }
+ FoundCandidateHeightStatus => None,
+ InitialCandidateHeightStatus => fail!(),
+ }
+ }
+}
+
+enum CandidateHeightIteratorStatus {
+ InitialCandidateHeightStatus,
+ TryingHeightCandidateHeightStatus,
+ TryingMaxCandidateHeightStatus,
+ TryingMinCandidateHeightStatus,
+ FoundCandidateHeightStatus,
+}
+
+// A helper function used in height calculation.
+fn translate_including_floats(cur_y: &mut Au, delta: Au, inorder: bool, floats: &mut Floats) {
+ *cur_y = *cur_y + delta;
+
+ if inorder {
+ floats.translate(Point2D(Au(0), -delta));
+ }
+}
+
/// The real assign-heights traversal for flows with position 'absolute'.
///
/// This is a traversal of an Absolute Flow tree.
@@ -330,6 +448,36 @@ enum BlockType {
FloatNonReplacedType,
}
+#[deriving(Clone, Eq)]
+pub enum MarginsMayCollapseFlag {
+ MarginsMayCollapse,
+ MarginsMayNotCollapse,
+}
+
+// Propagates the `layers_needed_for_descendants` flag appropriately from a child. This is called
+// as part of height assignment.
+//
+// If any fixed descendants of kids are present, this kid needs a layer.
+//
+// FIXME(#2006, pcwalton): This is too layer-happy. Like WebKit, we shouldn't do this unless
+// the positioned descendants are actually on top of the fixed kids.
+//
+// TODO(#1244, #2007, pcwalton): Do this for CSS transforms and opacity too, at least if they're
+// animating.
+fn propagate_layer_flag_from_child(layers_needed_for_descendants: &mut bool, kid: &mut Flow) {
+ if kid.is_absolute_containing_block() {
+ let kid_base = flow::mut_base(kid);
+ if kid_base.flags_info.flags.needs_layer() {
+ *layers_needed_for_descendants = true
+ }
+ } else {
+ let kid_base = flow::mut_base(kid);
+ if kid_base.flags_info.flags.layers_needed_for_descendants() {
+ *layers_needed_for_descendants = true
+ }
+ }
+}
+
// A block formatting context.
pub struct BlockFlow {
/// Data common to all flows.
@@ -350,9 +498,7 @@ pub struct BlockFlow {
}
impl BlockFlow {
- pub fn from_node(constructor: &mut FlowConstructor,
- node: &ThreadSafeLayoutNode)
- -> BlockFlow {
+ pub fn from_node(constructor: &mut FlowConstructor, node: &ThreadSafeLayoutNode) -> BlockFlow {
BlockFlow {
base: BaseFlow::new((*node).clone()),
box_: Some(Box::new(constructor, node)),
@@ -362,9 +508,7 @@ impl BlockFlow {
}
}
- pub fn from_node_and_box(node: &ThreadSafeLayoutNode,
- box_: Box)
- -> BlockFlow {
+ pub fn from_node_and_box(node: &ThreadSafeLayoutNode, box_: Box) -> BlockFlow {
BlockFlow {
base: BaseFlow::new((*node).clone()),
box_: Some(box_),
@@ -574,8 +718,8 @@ impl BlockFlow {
/// This is where we use the preferred widths and minimum widths
/// calculated in the bubble-widths traversal.
fn get_shrink_to_fit_width(&self, available_width: Au) -> Au {
- geometry::min(self.base.pref_width,
- geometry::max(self.base.min_width, available_width))
+ geometry::min(self.base.intrinsic_widths.preferred_width,
+ geometry::max(self.base.intrinsic_widths.minimum_width, available_width))
}
/// Collect and update static y-offsets bubbled up by kids.
@@ -589,19 +733,11 @@ impl BlockFlow {
/// position already set.
fn collect_static_y_offsets_from_kids(&mut self) {
let mut abs_descendant_y_offsets = SmallVec0::new();
- let mut fixed_descendant_y_offsets = SmallVec0::new();
-
for kid in self.base.child_iter() {
let mut gives_abs_offsets = true;
if kid.is_block_like() {
let kid_block = kid.as_block();
- if kid_block.is_fixed() {
- // It won't contribute any offsets for position 'absolute'
- // descendants because it would be the CB for them.
- gives_abs_offsets = false;
- // Add the offset for the current fixed flow too.
- fixed_descendant_y_offsets.push(kid_block.get_hypothetical_top_edge());
- } else if kid_block.is_absolutely_positioned() {
+ if kid_block.is_fixed() || kid_block.is_absolutely_positioned() {
// It won't contribute any offsets for descendants because it
// would be the CB for them.
gives_abs_offsets = false;
@@ -623,356 +759,289 @@ impl BlockFlow {
abs_descendant_y_offsets.push(y_offset);
}
}
-
- // Get all the fixed offsets.
- let kid_base = flow::mut_base(kid);
- // Consume all the static y-offsets bubbled up by kid.
- for y_offset in kid_base.fixed_descendants.static_y_offsets.move_iter() {
- // The offsets are wrt the kid flow box. Translate them to current flow.
- y_offset = y_offset + kid_base.position.origin.y;
- fixed_descendant_y_offsets.push(y_offset);
- }
}
self.base.abs_descendants.static_y_offsets = abs_descendant_y_offsets;
- self.base.fixed_descendants.static_y_offsets = fixed_descendant_y_offsets;
}
- /// Calculate clearance, top_offset, bottom_offset, and left_offset for the box.
- /// If `ignore_clear` is true, clearance does not need to be calculated.
- pub fn initialize_offsets(&mut self, ignore_clear: bool) -> (Au, Au, Au, Au) {
- match self.box_ {
- None => (Au(0), Au(0), Au(0), Au(0)),
- Some(ref box_) => {
- let clearance = match box_.clear() {
- Some(clear) if !ignore_clear => self.base.floats.clearance(clear),
- _ => Au::new(0)
- };
+ /// If this is the root flow, shifts all kids down and adjusts our size to account for
+ /// collapsed margins.
+ ///
+ /// TODO(#2017, pcwalton): This is somewhat inefficient (traverses kids twice); can we do
+ /// better?
+ fn adjust_boxes_for_collapsed_margins_if_root(&mut self) {
+ if !self.is_root() {
+ return
+ }
- // Offsets to content edge of box_
- let top_offset = clearance + box_.top_offset();
- let bottom_offset = box_.bottom_offset();
- let left_offset = box_.left_offset();
-
- (clearance, top_offset, bottom_offset, left_offset)
- }
- }
- }
-
- /// In case of inorder assign_height traversal and not absolute flow,
- /// 'assign_height's of all children are visited
- /// and Float info is shared between adjacent children.
- /// Float info of the last child is saved in parent flow.
- pub fn handle_children_floats_if_necessary(&mut self,
- ctx: &mut LayoutContext,
- inorder: bool,
- left_offset: Au,
- top_offset: Au) {
- // Note: Ignoring floats for absolute flow as of now.
- if inorder && !self.is_absolutely_positioned() {
- // Floats for blocks work like this:
- // self.floats -> child[0].floats
- // visit child[0]
- // child[i-1].floats -> child[i].floats
- // visit child[i]
- // repeat until all children are visited.
- // last_child.floats -> self.floats (done at the end of this method)
- self.base.floats.translate(Point2D(-left_offset, -top_offset));
- let mut floats = self.base.floats.clone();
+ let (top_margin_value, bottom_margin_value) = match self.base.collapsible_margins {
+ MarginsCollapseThrough(margin) => (Au(0), margin.collapse()),
+ MarginsCollapse(top_margin, bottom_margin) => {
+ (top_margin.collapse(), bottom_margin.collapse())
+ }
+ NoCollapsibleMargins(top, bottom) => (top, bottom),
+ };
+
+ // Shift all kids down (or up, if margins are negative) if necessary.
+ if top_margin_value != Au(0) {
for kid in self.base.child_iter() {
- flow::mut_base(kid).floats = floats;
- kid.assign_height_inorder(ctx);
- floats = flow::mut_base(kid).floats.clone();
+ let kid_base = flow::mut_base(kid);
+ kid_base.position.origin.y = kid_base.position.origin.y + top_margin_value
}
- self.base.floats = floats;
+ }
+
+ self.base.position.size.height = self.base.position.size.height + top_margin_value +
+ bottom_margin_value;
+
+ for fragment in self.box_.iter() {
+ let mut position = fragment.border_box.get();
+ position.size.height = position.size.height + top_margin_value + bottom_margin_value;
+ fragment.border_box.set(position);
}
}
- /// Compute margin_top and margin_bottom. Also, it is decided whether top margin and
- /// bottom margin are collapsible according to CSS 2.1 § 8.3.1.
- pub fn precompute_margin(&mut self) -> (Au, Au, bool, bool) {
- match self.box_ {
- // Margins for an absolutely positioned element do not collapse with
- // its children.
- Some(ref box_) if !self.is_absolutely_positioned() => {
- let top_margin_collapsible = !self.is_root &&
- box_.border.get().top == Au(0) &&
- box_.padding.get().top == Au(0);
-
- let bottom_margin_collapsible = !self.is_root &&
- box_.border.get().bottom == Au(0) &&
- box_.padding.get().bottom == Au(0);
-
- let margin_top = box_.margin.get().top;
- let margin_bottom = box_.margin.get().bottom;
-
- (margin_top, margin_bottom, top_margin_collapsible, bottom_margin_collapsible)
- },
- _ => (Au(0), Au(0), false, false)
- }
- }
-
- /// Compute collapsed margins between adjacent children or between the first/last child and parent
- /// according to CSS 2.1 § 8.3.1. Current y position(cur_y) is continually updated for collapsing result.
- pub fn compute_margin_collapse(&mut self,
- cur_y: &mut Au,
- top_offset: &mut Au,
- margin_top: &mut Au,
- margin_bottom: &mut Au,
- top_margin_collapsible: bool,
- bottom_margin_collapsible: bool) -> Au {
- // How much to move up by to get to the beginning of
- // current kid flow.
- // Example: if previous sibling's margin-bottom is 20px and your
- // margin-top is 12px, the collapsed margin will be 20px. Since cur_y
- // will be at the bottom margin edge of the previous sibling, we have
- // to move up by 12px to get to our top margin edge. So, `collapsing`
- // will be set to 12px
- let mut first_in_flow = true;
- let mut collapsing = Au::new(0);
- // The amount of margin that we can potentially collapse with
- let mut collapsible = if top_margin_collapsible {
- *margin_top
- } else {
- Au(0)
- };
+ /// Assign height for current flow.
+ ///
+ /// * Collapse margins for flow's children and set in-flow child flows' y-coordinates now that
+ /// we know their heights.
+ /// * Calculate and set the height of the current flow.
+ /// * Calculate height, vertical margins, and y-coordinate for the flow's box. Ideally, this
+ /// should be calculated using CSS § 10.6.7.
+ ///
+ /// For absolute flows, we store the calculated content height for the flow. We defer the
+ /// calculation of the other values until a later traversal.
+ ///
+ /// `inline(always)` because this is only ever called by in-order or non-in-order top-level
+ /// methods
+ #[inline(always)]
+ pub fn assign_height_block_base(&mut self,
+ layout_context: &mut LayoutContext,
+ inorder: bool,
+ margins_may_collapse: MarginsMayCollapseFlag) {
+ // Our current border-box position.
+ let mut cur_y = Au(0);
+
+ // The sum of our top border and top padding.
+ let mut top_offset = Au(0);
- // At this point, cur_y is at the content edge of the flow's box_
+ // Absolute positioning establishes a block formatting context. Don't propagate floats
+ // in or out. (But do propagate them between kids.)
+ if inorder && self.is_absolutely_positioned() {
+ self.base.floats = Floats::new();
+ }
+ if margins_may_collapse != MarginsMayCollapse {
+ self.base.floats = Floats::new();
+ }
+
+ let mut margin_collapse_info = MarginCollapseInfo::new();
+ for fragment in self.box_.iter() {
+ self.base.floats.translate(Point2D(-fragment.left_offset(), Au(0)));
+
+ top_offset = fragment.border.get().top + fragment.padding.get().top;
+ translate_including_floats(&mut cur_y, top_offset, inorder, &mut self.base.floats);
+
+ let can_collapse_top_margin_with_kids =
+ margins_may_collapse == MarginsMayCollapse &&
+ !self.is_absolutely_positioned() &&
+ fragment.border.get().top == Au(0) &&
+ fragment.padding.get().top == Au(0);
+ margin_collapse_info.initialize_top_margin(fragment,
+ can_collapse_top_margin_with_kids);
+ }
+
+ // At this point, cur_y is at the content edge of the flow's box.
+ let mut floats = self.base.floats.clone();
+ let mut layers_needed_for_descendants = false;
for kid in self.base.child_iter() {
- // At this point, cur_y is at bottom margin edge of previous kid
if kid.is_absolutely_positioned() {
- // Assume that the `hypothetical box` for an absolute flow
- // starts immediately after the bottom margin edge of the
- // previous flow.
- kid.as_block().base.position.origin.y = *cur_y;
- // Skip the collapsing for absolute flow kids and continue
+ // Assume that the *hypothetical box* for an absolute flow starts immediately after
+ // the bottom border edge of the previous flow.
+ kid.as_block().base.position.origin.y = cur_y;
+
+ if inorder {
+ kid.assign_height_inorder(layout_context)
+ }
+
+ propagate_layer_flag_from_child(&mut layers_needed_for_descendants, kid);
+
+ // Skip the collapsing and float processing for absolute flow kids and continue
// with the next flow.
- } else {
- kid.collapse_margins(top_margin_collapsible,
- &mut first_in_flow,
- margin_top,
- top_offset,
- &mut collapsing,
- &mut collapsible);
- let child_node = flow::mut_base(kid);
- *cur_y = *cur_y - collapsing;
- // At this point, after moving up by `collapsing`, cur_y is at the
- // top margin edge of kid
- child_node.position.origin.y = *cur_y;
- *cur_y = *cur_y + child_node.position.size.height;
- // At this point, cur_y is at the bottom margin edge of kid
+ continue
}
- }
- self.collect_static_y_offsets_from_kids();
+ // Assign height now for the child if it was impacted by floats and we couldn't before.
+ let mut floats_out = None;
+ if inorder {
+ if !kid.is_float() {
+ let kid_base = flow::mut_base(kid);
+ if kid_base.clear != clear::none {
+ // We have clearance, so assume there are no floats in and perform layout.
+ //
+ // FIXME(#2008, pcwalton): This could be wrong if we have `clear: left` or
+ // `clear: right` and there are still floats to impact, of course. But this
+ // gets complicated with margin collapse. Possibly the right thing to do is
+ // to lay out the block again in this rare case. (Note that WebKit can lay
+ // blocks out twice; this may be related, although I haven't looked into it
+ // closely.)
+ kid_base.floats = Floats::new()
+ } else {
+ kid_base.floats = floats.clone()
+ }
+ } else {
+ let kid_base = flow::mut_base(kid);
+ kid_base.position.origin.y = margin_collapse_info.current_float_ceiling();
+ kid_base.floats = floats.clone()
+ }
+
+ kid.assign_height_inorder(layout_context);
+
+ floats_out = Some(flow::mut_base(kid).floats.clone());
+
+ // A horrible hack that has to do with the fact that `origin.y` is used for
+ // something else later (containing block for float).
+ if kid.is_float() {
+ flow::mut_base(kid).position.origin.y = cur_y;
+ }
+ }
- // The bottom margin collapses with its last in-flow block-level child's bottom margin
- // if the parent has no bottom border, no bottom padding.
- // The bottom margin for an absolutely positioned element does not
- // collapse even with its children.
- collapsing = if bottom_margin_collapsible && !self.is_absolutely_positioned() {
- if *margin_bottom < collapsible {
- *margin_bottom = collapsible;
+ propagate_layer_flag_from_child(&mut layers_needed_for_descendants, kid);
+
+ // If the child was a float, stop here.
+ if kid.is_float() {
+ if inorder {
+ floats = floats_out.take_unwrap();
+ }
+ continue
}
- collapsible
- } else {
- Au::new(0)
- };
- collapsing
- }
+ // Handle any (possibly collapsed) top margin.
+ let kid_base = flow::mut_base(kid);
+ let delta = margin_collapse_info.advance_top_margin(&kid_base.collapsible_margins);
+ translate_including_floats(&mut cur_y, delta, inorder, &mut floats);
+
+ // Clear past floats, if necessary.
+ if inorder {
+ let clearance = match kid_base.clear {
+ clear::none => Au(0),
+ clear::left => floats.clearance(ClearLeft),
+ clear::right => floats.clearance(ClearRight),
+ clear::both => floats.clearance(ClearBoth),
+ };
+ cur_y = cur_y + clearance
+ }
+
+ // At this point, `cur_y` is at the border edge of the child.
+ assert!(kid_base.position.origin.y == Au(0));
+ kid_base.position.origin.y = cur_y;
+
+ // If this was an inorder traversal, grab the child's floats now.
+ if inorder {
+ floats = floats_out.take_unwrap()
+ }
+
+ // Move past the child's border box. Do not use the `translate_including_floats`
+ // function here because the child has already translated floats past its border box.
+ cur_y = cur_y + kid_base.position.size.height;
+
+ // Handle any (possibly collapsed) bottom margin.
+ let delta = margin_collapse_info.advance_bottom_margin(&kid_base.collapsible_margins);
+ translate_including_floats(&mut cur_y, delta, inorder, &mut floats);
+ }
+
+ self.base
+ .flags_info
+ .flags
+ .set_layers_needed_for_descendants(layers_needed_for_descendants);
+
+ self.collect_static_y_offsets_from_kids();
+
+ // Add in our bottom margin and compute our collapsible margins.
+ for fragment in self.box_.iter() {
+ let can_collapse_bottom_margin_with_kids =
+ margins_may_collapse == MarginsMayCollapse &&
+ !self.is_absolutely_positioned() &&
+ fragment.border.get().bottom == Au(0) &&
+ fragment.padding.get().bottom == Au(0);
+ let (collapsible_margins, delta) =
+ margin_collapse_info.finish_and_compute_collapsible_margins(
+ fragment,
+ can_collapse_bottom_margin_with_kids);
+ self.base.collapsible_margins = collapsible_margins;
+ translate_including_floats(&mut cur_y, delta, inorder, &mut floats);
+ }
+
+ // FIXME(#2003, pcwalton): The max is taken here so that you can scroll the page, but this
+ // is not correct behavior according to CSS 2.1 § 10.5. Instead I think we should treat the
+ // root element as having `overflow: scroll` and use the layers-based scrolling
+ // infrastructure to make it scrollable.
+ let mut height = cur_y - top_offset;
+ if self.is_root() {
+ height = Au::max(layout_context.screen_size.height, height)
+ }
- /// For an absolutely positioned element, store the content height for use in calculating
- /// the absolute flow's dimensions later.
- pub fn store_content_height_if_absolutely_positioned(&mut self,
- height: Au) -> bool {
if self.is_absolutely_positioned() {
+ // The content height includes all the floats per CSS 2.1 § 10.6.7. The easiest way to
+ // handle this is to just treat this as clearance.
+ height = height + floats.clearance(ClearBoth);
+
+ // Fixed position layers get layers.
+ if self.is_fixed() {
+ self.base.flags_info.flags.set_needs_layer(true)
+ }
+
+ // Store the content height for use in calculating the absolute flow's dimensions
+ // later.
for box_ in self.box_.iter() {
let mut temp_position = box_.border_box.get();
temp_position.size.height = height;
box_.border_box.set(temp_position);
}
- return true;
+ return
}
- false
- }
-
- /// Compute the box height and set border_box and margin of the box.
- pub fn compute_height_position(&mut self,
- height: &mut Au,
- border_and_padding: Au,
- margin_top: Au,
- margin_bottom: Au,
- clearance: Au) {
- // Here, height is content height of box_
- let mut noncontent_height = border_and_padding;
- for box_ in self.box_.iter() {
- let mut position = box_.border_box.get();
- let mut margin = box_.margin.get();
- // The associated box is the border box of this flow.
- // Margin after collapse
- margin.top = margin_top;
- margin.bottom = margin_bottom;
+ for fragment in self.box_.iter() {
+ let mut candidate_height_iterator = CandidateHeightIterator::new(fragment.style(),
+ None);
+ for (candidate_height, new_candidate_height) in candidate_height_iterator {
+ *new_candidate_height = match candidate_height {
+ Auto => height,
+ Specified(value) => value
+ }
+ }
- position.origin.y = clearance + margin.top;
- // Border box height
- position.size.height = *height + noncontent_height;
+ // Adjust `cur_y` as necessary to account for the explicitly-specified height.
+ height = candidate_height_iterator.candidate_value;
+ let delta = height - (cur_y - top_offset);
+ translate_including_floats(&mut cur_y, delta, inorder, &mut floats);
- noncontent_height = noncontent_height + clearance + margin.top + margin.bottom;
+ // Compute content height and noncontent height.
+ let bottom_offset = fragment.border.get().bottom + fragment.padding.get().bottom;
+ translate_including_floats(&mut cur_y, bottom_offset, inorder, &mut floats);
- box_.border_box.set(position);
- box_.margin.set(margin);
+ // Now that `cur_y` is at the bottom of the border box, compute the final border box
+ // position.
+ let mut border_box = fragment.border_box.get();
+ border_box.size.height = cur_y;
+ border_box.origin.y = Au(0);
+ fragment.border_box.set(border_box);
+ self.base.position.size.height = cur_y;
}
- // Height of margin box + clearance
- self.base.position.size.height = *height + noncontent_height;
- }
+ self.base.floats = floats.clone();
+ self.adjust_boxes_for_collapsed_margins_if_root();
- /// Set floats_out at the last step of the assign height calculation.
- pub fn set_floats_out_if_inorder(&mut self,
- inorder: bool,
- height: Au,
- cur_y: Au,
- top_offset: Au,
- bottom_offset: Au,
- left_offset: Au) {
- if inorder {
- let extra_height = height - (cur_y - top_offset) + bottom_offset;
- self.base.floats.translate(Point2D(left_offset, -extra_height));
- }
- }
-
- /// Assign heights for all flows in absolute flow tree and store overflow for all
- /// absolute descendants.
- pub fn assign_height_absolute_flows(&mut self, ctx: &mut LayoutContext) {
if self.is_root_of_absolute_flow_tree() {
// Assign heights for all flows in this Absolute flow tree.
// This is preorder because the height of an absolute flow may depend on
// the height of its CB, which may also be an absolute flow.
- self.traverse_preorder_absolute_flows(&mut AbsoluteAssignHeightsTraversal(ctx));
+ self.traverse_preorder_absolute_flows(&mut AbsoluteAssignHeightsTraversal(
+ layout_context));
// Store overflow for all absolute descendants.
self.traverse_postorder_absolute_flows(&mut AbsoluteStoreOverflowTraversal {
- layout_context: ctx,
+ layout_context: layout_context,
});
}
}
- /// Assign height for current flow.
- ///
- /// + Collapse margins for flow's children and set in-flow child flows'
- /// y-coordinates now that we know their heights.
- /// + Calculate and set the height of the current flow.
- /// + Calculate height, vertical margins, and y-coordinate for the flow's
- /// box. Ideally, this should be calculated using CSS Section 10.6.7
- ///
- /// For absolute flows, store the calculated content height for the flow.
- /// Defer the calculation of the other values till a later traversal.
- ///
- /// inline(always) because this is only ever called by in-order or non-in-order top-level
- /// methods
- #[inline(always)]
- fn assign_height_block_base(&mut self, ctx: &mut LayoutContext, inorder: bool) {
-
- // Note: Ignoring clearance for absolute flows as of now.
- let ignore_clear = self.is_absolutely_positioned();
- let (clearance, mut top_offset, bottom_offset, left_offset) =
- self.initialize_offsets(ignore_clear);
-
- self.handle_children_floats_if_necessary(ctx, inorder,
- left_offset, top_offset);
-
- let (mut margin_top, mut margin_bottom,
- top_margin_collapsible, bottom_margin_collapsible) = self.precompute_margin();
-
- let mut cur_y = top_offset;
- let collapsing = self.compute_margin_collapse(&mut cur_y,
- &mut top_offset,
- &mut margin_top,
- &mut margin_bottom,
- top_margin_collapsible,
- bottom_margin_collapsible);
-
- // TODO: A box's own margins collapse if the 'min-height' property is zero, and it has neither
- // top or bottom borders nor top or bottom padding, and it has a 'height' of either 0 or 'auto',
- // and it does not contain a line box, and all of its in-flow children's margins (if any) collapse.
-
- let screen_height = ctx.screen_size.height;
-
- let mut height = if self.is_root() {
- // FIXME(pcwalton): The max is taken here so that you can scroll the page, but this is
- // not correct behavior according to CSS 2.1 § 10.5. Instead I think we should treat
- // the root element as having `overflow: scroll` and use the layers-based scrolling
- // infrastructure to make it scrollable.
- Au::max(screen_height, cur_y)
- } else {
- // (cur_y - collapsing) will get you the the bottom margin-edge of
- // the bottom-most child.
- // top_offset: top margin-edge of the topmost child.
- // hence, height = content height
- cur_y - top_offset - collapsing
- };
-
- // For an absolutely positioned element, store the content height and stop the function.
- if self.store_content_height_if_absolutely_positioned(height) {
- return;
- }
-
- let mut border_and_padding = Au::new(0);
- for box_ in self.box_.iter() {
- let style = box_.style();
-
- // At this point, `height` is the height of the containing block, so passing `height`
- // as the second argument here effectively makes percentages relative to the containing
- // block per CSS 2.1 § 10.5.
- height = match MaybeAuto::from_style(style.Box.get().height, height) {
- Auto => height,
- Specified(value) => value
- };
-
- border_and_padding = box_.padding.get().top + box_.padding.get().bottom +
- box_.border.get().top + box_.border.get().bottom;
- }
-
- self.compute_height_position(&mut height,
- border_and_padding,
- margin_top,
- margin_bottom,
- clearance);
-
- self.set_floats_out_if_inorder(inorder, height, cur_y, top_offset, bottom_offset, left_offset);
- self.assign_height_absolute_flows(ctx);
- if self.is_root() {
- self.assign_height_store_overflow_fixed_flows(ctx);
- }
- }
-
- /// Assign height for all fixed descendants.
- ///
- /// A flat iteration over all fixed descendants, passing their respective
- /// static y offsets.
- /// Also, store overflow immediately because nothing else depends on a
- /// fixed flow's height.
- fn assign_height_store_overflow_fixed_flows(&mut self, ctx: &mut LayoutContext) {
- assert!(self.is_root());
- let mut descendant_offset_iter = self.base.fixed_descendants.iter_with_offset();
- // Pass in the respective static y offset for each descendant.
- for (ref mut descendant_link, ref y_offset) in descendant_offset_iter {
- match descendant_link.resolve() {
- Some(fixed_flow) => {
- {
- let block = fixed_flow.as_block();
- // The stored y_offset is wrt to the flow box (which
- // will is also the CB, so it is the correct final value).
- block.static_y_offset = **y_offset;
- block.calculate_abs_height_and_margins(ctx);
- }
- fixed_flow.store_overflow(ctx);
- }
- None => fail!("empty Rawlink to a descendant")
- }
- }
- }
-
/// Add placement information about current float flow for use by the parent.
///
/// Also, use information given by parent about other floats to find out
@@ -1009,7 +1078,7 @@ impl BlockFlow {
let info = PlacementInfo {
size: Size2D(self.base.position.size.width + full_noncontent_width,
height + margin_height),
- ceiling: clearance,
+ ceiling: clearance + self.base.position.origin.y,
max_width: self.float.get_ref().containing_width,
kind: self.float.get_ref().float_kind,
};
@@ -1060,7 +1129,7 @@ impl BlockFlow {
cur_y = cur_y + child_base.position.size.height;
}
- let mut height = cur_y - top_offset;
+ let content_height = cur_y - top_offset;
let mut noncontent_height;
let box_ = self.box_.as_ref().unwrap();
@@ -1072,107 +1141,83 @@ impl BlockFlow {
noncontent_height = box_.padding.get().top + box_.padding.get().bottom +
box_.border.get().top + box_.border.get().bottom;
- //TODO(eatkinson): compute heights properly using the 'height' property.
- let height_prop = MaybeAuto::from_style(box_.style().Box.get().height,
- Au::new(0)).specified_or_zero();
+ // Calculate content height, taking `min-height` and `max-height` into account.
- height = geometry::max(height, height_prop) + noncontent_height;
- debug!("assign_height_float -- height: {}", height);
+ let mut candidate_height_iterator = CandidateHeightIterator::new(box_.style(), None);
+ for (candidate_height, new_candidate_height) in candidate_height_iterator {
+ *new_candidate_height = match candidate_height {
+ Auto => content_height,
+ Specified(value) => value,
+ }
+ }
- position.size.height = height;
- box_.border_box.set(position);
- }
+ let content_height = candidate_height_iterator.candidate_value;
- /// In case of float, initialize containing_width at the beginning step of assign_width.
- pub fn set_containing_width_if_float(&mut self, containing_block_width: Au) {
- if self.is_float() {
- self.float.get_mut_ref().containing_width = containing_block_width;
+ debug!("assign_height_float -- height: {}", content_height + noncontent_height);
- // Parent usually sets this, but floats are never inorder
- self.base.flags_info.flags.set_inorder(false);
- }
+ position.size.height = content_height + noncontent_height;
+ box_.border_box.set(position);
}
- /// Assign the computed left_content_edge and content_width to children.
- pub fn propagate_assigned_width_to_children(&mut self, left_content_edge: Au,
- content_width: Au,
- opt_col_widths: Option<~[Au]>) {
- let has_inorder_children = if self.is_float() {
- self.base.num_floats > 0
- } else {
- self.base.flags_info.flags.inorder() || self.base.num_floats > 0
- };
-
- let kid_abs_cb_x_offset;
- if self.is_positioned() {
- match self.box_ {
- Some(ref box_) => {
- // Pass yourself as a new Containing Block
- // The static x offset for any immediate kid flows will be the
- // left padding
- kid_abs_cb_x_offset = box_.padding.get().left;
- }
- None => fail!("BlockFlow: no principal box found"),
+ fn build_display_list_block_common(&mut self,
+ stacking_context: &mut StackingContext,
+ builder: &mut DisplayListBuilder,
+ info: &DisplayListBuildingInfo,
+ offset: Point2D<Au>,
+ background_border_level: BackgroundAndBorderLevel) {
+ let mut info = *info;
+ let mut rel_offset = Point2D(Au(0), Au(0));
+ for fragment in self.box_.iter() {
+ rel_offset = fragment.relative_position(&info.relative_containing_block_size);
+
+ // Add the box that starts the block context.
+ fragment.build_display_list(stacking_context,
+ builder,
+ &info,
+ self.base.abs_position + rel_offset + offset,
+ (&*self) as &Flow,
+ background_border_level);
+
+ // For relatively-positioned descendants, the containing block formed by a block is
+ // just the content box. The containing block for absolutely-positioned descendants,
+ // on the other hand, only established if we are positioned.
+ info.relative_containing_block_size = fragment.content_box_size();
+ if self.is_positioned() {
+ info.absolute_containing_block_position =
+ self.base.abs_position +
+ self.generated_cb_position() +
+ fragment.relative_position(&info.relative_containing_block_size)
}
- } else {
- // For kids, the left margin edge will be at our left content edge.
- // The current static offset is at our left margin
- // edge. So move in to the left content edge.
- kid_abs_cb_x_offset = self.base.absolute_static_x_offset + left_content_edge;
}
- let kid_fixed_cb_x_offset = self.base.fixed_static_x_offset + left_content_edge;
-
- // FIXME(ksh8281): avoid copy
- let flags_info = self.base.flags_info.clone();
-
- // Left margin edge of kid flow is at our left content edge
- let mut kid_left_margin_edge = left_content_edge;
- // Width of kid flow is our content width
- let mut kid_width = content_width;
- for (i, kid) in self.base.child_iter().enumerate() {
- assert!(kid.is_block_flow() || kid.is_inline_flow() || kid.is_table_kind());
- match opt_col_widths {
- Some(ref col_widths) => {
- // If kid is table_rowgroup or table_row, the column widths info should be
- // copied from its parent.
- if kid.is_table_rowgroup() {
- kid.as_table_rowgroup().col_widths = col_widths.clone()
- } else if kid.is_table_row() {
- kid.as_table_row().col_widths = col_widths.clone()
- } else if kid.is_table_cell() {
- // If kid is table_cell, the x offset and width for each cell should be
- // calculated from parent's column widths info.
- kid_left_margin_edge = if i == 0 {
- Au(0)
- } else {
- kid_left_margin_edge + col_widths[i-1]
- };
- kid_width = col_widths[i]
- }
- }
- None => {}
- }
- if kid.is_block_flow() {
- let kid_block = kid.as_block();
- kid_block.base.absolute_static_x_offset = kid_abs_cb_x_offset;
- kid_block.base.fixed_static_x_offset = kid_fixed_cb_x_offset;
+ let this_position = self.base.abs_position;
+ for kid in self.base.child_iter() {
+ {
+ let kid_base = flow::mut_base(kid);
+ kid_base.abs_position = this_position + kid_base.position.origin + rel_offset +
+ offset;
}
- let child_base = flow::mut_base(kid);
- child_base.position.origin.x = kid_left_margin_edge;
- child_base.position.size.width = kid_width;
- child_base.flags_info.flags.set_inorder(has_inorder_children);
- if !child_base.flags_info.flags.inorder() {
- child_base.floats = Floats::new();
+ if kid.is_absolutely_positioned() {
+ // All absolute flows will be handled by their containing block.
+ continue
}
- // Per CSS 2.1 § 16.3.1, text decoration propagates to all children in flow.
- //
- // TODO(pcwalton): When we have out-of-flow children, don't unconditionally propagate.
+ kid.build_display_list(stacking_context, builder, &info);
+ }
- child_base.flags_info.propagate_text_decoration_from_parent(&flags_info);
- child_base.flags_info.propagate_text_alignment_from_parent(&flags_info)
+ // Process absolute descendant links.
+ let mut absolute_info = info;
+ absolute_info.layers_needed_for_positioned_flows =
+ self.base.flags_info.flags.layers_needed_for_descendants();
+ for abs_descendant_link in self.base.abs_descendants.iter() {
+ match abs_descendant_link.resolve() {
+ Some(flow) => {
+ // TODO(pradeep): Send in our absolute position directly.
+ flow.build_display_list(stacking_context, builder, &absolute_info)
+ }
+ None => fail!("empty Rawlink to a descendant")
+ }
}
}
@@ -1180,104 +1225,37 @@ impl BlockFlow {
///
/// Set the absolute position for children after doing any offsetting for
/// position: relative.
- pub fn build_display_list_block<E:ExtraDisplayListData>(
- &mut self,
- builder: &DisplayListBuilder,
- container_block_size: &Size2D<Au>,
- absolute_cb_abs_position: Point2D<Au>,
- dirty: &Rect<Au>,
- index: uint,
- lists: &RefCell<DisplayListCollection<E>>)
- -> uint {
-
+ pub fn build_display_list_block(&mut self,
+ stacking_context: &mut StackingContext,
+ builder: &mut DisplayListBuilder,
+ info: &DisplayListBuildingInfo) {
if self.is_float() {
- self.build_display_list_float(builder, container_block_size, dirty, index, lists);
- return index;
+ // TODO(#2009, pcwalton): This is a pseudo-stacking context. We need to merge `z-index:
+ // auto` kids into the parent stacking context, when that is supported.
+ self.build_display_list_float(stacking_context, builder, info)
} else if self.is_absolutely_positioned() {
- return self.build_display_list_abs(builder, container_block_size,
- absolute_cb_abs_position,
- dirty, index, lists);
- }
-
- // FIXME: Shouldn't this be the abs_rect _after_ relative positioning?
- let abs_rect = Rect(self.base.abs_position, self.base.position.size);
- if !abs_rect.intersects(dirty) {
- return index;
- }
-
- debug!("build_display_list_block: adding display element");
-
- let rel_offset = match self.box_ {
- Some(ref box_) => {
- box_.relative_position(container_block_size)
- },
- None => {
- Point2D {
- x: Au::new(0),
- y: Au::new(0),
- }
- }
- };
-
- // add box that starts block context
- for box_ in self.box_.iter() {
- box_.build_display_list(builder, dirty, self.base.abs_position + rel_offset,
- (&*self) as &Flow, index, lists);
- }
-
- // TODO: handle any out-of-flow elements
- let this_position = self.base.abs_position;
- for child in self.base.child_iter() {
- let child_base = flow::mut_base(child);
- child_base.abs_position = this_position + child_base.position.origin + rel_offset;
+ self.build_display_list_abs(stacking_context, builder, info)
+ } else {
+ self.build_display_list_block_common(stacking_context,
+ builder,
+ info,
+ Point2D(Au(0), Au(0)),
+ BlockLevel)
}
-
- index
}
- pub fn build_display_list_float<E:ExtraDisplayListData>(
- &mut self,
- builder: &DisplayListBuilder,
- container_block_size: &Size2D<Au>,
- dirty: &Rect<Au>,
- index: uint,
- lists: &RefCell<DisplayListCollection<E>>)
- -> bool {
- let abs_rect = Rect(self.base.abs_position, self.base.position.size);
- if !abs_rect.intersects(dirty) {
- return true;
- }
-
- // position:relative
- let rel_offset = match self.box_ {
- Some(ref box_) => {
- box_.relative_position(container_block_size)
- },
- None => {
- Point2D {
- x: Au::new(0),
- y: Au::new(0),
- }
- }
- };
-
-
- let offset = self.base.abs_position + self.float.get_ref().rel_pos + rel_offset;
- // add box that starts block context
- for box_ in self.box_.iter() {
- box_.build_display_list(builder, dirty, offset, (&*self) as &Flow, index, lists);
- }
-
-
- // TODO: handle any out-of-flow elements
-
- // go deeper into the flow tree
- for child in self.base.child_iter() {
- let child_base = flow::mut_base(child);
- child_base.abs_position = offset + child_base.position.origin + rel_offset;
- }
-
- false
+ pub fn build_display_list_float(&mut self,
+ parent_stacking_context: &mut StackingContext,
+ builder: &mut DisplayListBuilder,
+ info: &DisplayListBuildingInfo) {
+ let mut stacking_context = StackingContext::new();
+ let float_offset = self.float.get_ref().rel_pos;
+ self.build_display_list_block_common(&mut stacking_context,
+ builder,
+ info,
+ float_offset,
+ RootOfStackingContextLevel);
+ parent_stacking_context.floats.push_all_move(stacking_context.flatten())
}
/// Calculate and set the height, offsets, etc. for absolutely positioned flow.
@@ -1293,12 +1271,10 @@ impl BlockFlow {
for box_ in self.box_.iter() {
// This is the stored content height value from assign-height
- let content_height = box_.border_box.get().size.height;
+ let content_height = box_.border_box.get().size.height - box_.noncontent_height();
let style = box_.style();
- let height_used_val = MaybeAuto::from_style(style.Box.get().height, containing_block_height);
-
// Non-auto margin-top and margin-bottom values have already been
// calculated during assign-width.
let margin = box_.margin.get();
@@ -1316,7 +1292,8 @@ impl BlockFlow {
MaybeAuto::from_style(style.PositionOffsets.get().bottom, containing_block_height));
let available_height = containing_block_height - box_.noncontent_height();
- let solution = if self.is_replaced_content() {
+ let mut solution = None;
+ if self.is_replaced_content() {
// Calculate used value of height just like we do for inline replaced elements.
// TODO: Pass in the containing block height when Box's
// assign-height can handle it correctly.
@@ -1325,25 +1302,36 @@ impl BlockFlow {
// margin because of erroneous height calculation in Box_.
// Check this when that has been fixed.
let height_used_val = box_.border_box.get().size.height;
- HeightConstraintSolution::solve_vertical_constraints_abs_replaced(height_used_val,
- margin_top,
- margin_bottom,
- top,
- bottom,
- content_height,
- available_height,
- static_y_offset)
+ solution = Some(HeightConstraintSolution::solve_vertical_constraints_abs_replaced(
+ height_used_val,
+ margin_top,
+ margin_bottom,
+ top,
+ bottom,
+ content_height,
+ available_height,
+ static_y_offset));
} else {
- HeightConstraintSolution::solve_vertical_constraints_abs_nonreplaced(
- height_used_val,
- margin_top,
- margin_bottom,
- top,
- bottom,
- content_height,
- available_height,
- static_y_offset)
- };
+ let mut candidate_height_iterator =
+ CandidateHeightIterator::new(style, Some(containing_block_height));
+
+ for (height_used_val, new_candidate_height) in candidate_height_iterator {
+ solution =
+ Some(HeightConstraintSolution::solve_vertical_constraints_abs_nonreplaced(
+ height_used_val,
+ margin_top,
+ margin_bottom,
+ top,
+ bottom,
+ content_height,
+ available_height,
+ static_y_offset));
+
+ *new_candidate_height = solution.unwrap().height
+ }
+ }
+
+ let solution = solution.unwrap();
let mut margin = box_.margin.get();
margin.top = solution.margin_top;
@@ -1351,63 +1339,70 @@ impl BlockFlow {
box_.margin.set(margin);
let mut position = box_.border_box.get();
- position.origin.y = box_.margin.get().top;
+ position.origin.y = Au(0);
// Border box height
let border_and_padding = box_.noncontent_height();
position.size.height = solution.height + border_and_padding;
box_.border_box.set(position);
- self.base.position.origin.y = solution.top;
- self.base.position.size.height = solution.height + border_and_padding
- + solution.margin_top + solution.margin_bottom;
+ self.base.position.origin.y = solution.top + margin.top;
+ self.base.position.size.height = solution.height + border_and_padding;
}
}
/// Add display items for Absolutely Positioned flow.
- pub fn build_display_list_abs<E:ExtraDisplayListData>(
- &mut self,
- builder: &DisplayListBuilder,
- _: &Size2D<Au>,
- absolute_cb_abs_position: Point2D<Au>,
- dirty: &Rect<Au>,
- mut index: uint,
- lists: &RefCell<DisplayListCollection<E>>)
- -> uint {
- let flow_origin = if self.is_fixed() {
+ pub fn build_display_list_abs(&mut self,
+ parent_stacking_context: &mut StackingContext,
+ builder: &mut DisplayListBuilder,
+ info: &DisplayListBuildingInfo) {
+ let mut stacking_context = StackingContext::new();
+ let mut info = *info;
+
+ info.absolute_containing_block_position = if self.is_fixed() {
// The viewport is initially at (0, 0).
self.base.position.origin
} else {
// Absolute position of Containing Block + position of absolute flow
// wrt Containing Block
- absolute_cb_abs_position + self.base.position.origin
+ info.absolute_containing_block_position + self.base.position.origin
};
- if self.is_fixed() {
- lists.with_mut(|lists| {
- index = lists.lists.len();
- lists.add_list(DisplayList::<E>::new());
- });
- }
-
// Set the absolute position, which will be passed down later as part
// of containing block details for absolute descendants.
- self.base.abs_position = flow_origin;
- let abs_rect = Rect(flow_origin, self.base.position.size);
- if !abs_rect.intersects(dirty) {
- return index;
- }
-
- for box_ in self.box_.iter() {
- box_.build_display_list(builder, dirty, flow_origin, (&*self) as &Flow, index, lists);
- }
+ self.base.abs_position = info.absolute_containing_block_position;
- // Go deeper into the flow tree.
- for child in self.base.child_iter() {
- let child_base = flow::mut_base(child);
- child_base.abs_position = flow_origin + child_base.position.origin;
- }
+ self.build_display_list_block_common(&mut stacking_context,
+ builder,
+ &info,
+ Point2D(Au(0), Au(0)),
+ RootOfStackingContextLevel);
- index
+ if !info.layers_needed_for_positioned_flows && !self.base.flags_info.flags.needs_layer() {
+ // We didn't need a layer.
+ //
+ // TODO(#781, pcwalton): `z-index`.
+ parent_stacking_context.positioned_descendants.push((0, stacking_context.flatten()));
+ return
+ }
+
+ // If we got here, then we need a new layer.
+ let size = Size2D(self.base.position.size.width.to_nearest_px() as uint,
+ self.base.position.size.height.to_nearest_px() as uint);
+ let origin = Point2D(info.absolute_containing_block_position.x.to_nearest_px() as uint,
+ info.absolute_containing_block_position.y.to_nearest_px() as uint);
+ let scroll_policy = if self.is_fixed() {
+ FixedPosition
+ } else {
+ Scrollable
+ };
+ let new_layer = RenderLayer {
+ id: self.layer_id(0),
+ display_list: Arc::new(stacking_context.flatten()),
+ position: Rect(origin, size),
+ background_color: color::rgba(255.0, 255.0, 255.0, 0.0),
+ scroll_policy: scroll_policy,
+ };
+ builder.layers.push(new_layer)
}
/// Return the top outer edge of the Hypothetical Box for an absolute flow.
@@ -1420,6 +1415,113 @@ impl BlockFlow {
fn get_hypothetical_top_edge(&self) -> Au {
self.base.position.origin.y
}
+
+ /// Initializes the containing width if this block flow is a float. This is done at the start
+ /// of `assign_widths`.
+ fn set_containing_width_if_float(&mut self, containing_block_width: Au) {
+ if self.is_float() {
+ self.float.get_mut_ref().containing_width = containing_block_width;
+
+ // Parent usually sets this, but floats are never inorder
+ self.base.flags_info.flags.set_inorder(false);
+ }
+ }
+
+ /// Assigns the computed left content edge and width to all the children of this block flow.
+ pub fn propagate_assigned_width_to_children(&mut self,
+ left_content_edge: Au,
+ content_width: Au,
+ opt_col_widths: Option<~[Au]>) {
+ let has_inorder_children = if self.is_float() {
+ self.base.num_floats > 0
+ } else {
+ self.base.flags_info.flags.inorder() || self.base.num_floats > 0
+ };
+
+ let kid_abs_cb_x_offset;
+ if self.is_positioned() {
+ match self.box_ {
+ Some(ref box_) => {
+ // Pass yourself as a new Containing Block
+ // The static x offset for any immediate kid flows will be the
+ // left padding
+ kid_abs_cb_x_offset = box_.padding.get().left;
+ }
+ None => fail!("BlockFlow: no principal box found"),
+ }
+ } else {
+ // For kids, the left margin edge will be at our left content edge.
+ // The current static offset is at our left margin
+ // edge. So move in to the left content edge.
+ kid_abs_cb_x_offset = self.base.absolute_static_x_offset + left_content_edge;
+ }
+ let kid_fixed_cb_x_offset = self.base.fixed_static_x_offset + left_content_edge;
+
+ // This value is used only for table cells.
+ let mut kid_left_margin_edge = left_content_edge;
+
+ let flags_info = self.base.flags_info.clone();
+ for (i, kid) in self.base.child_iter().enumerate() {
+ if kid.is_block_flow() {
+ let kid_block = kid.as_block();
+ kid_block.base.absolute_static_x_offset = kid_abs_cb_x_offset;
+ kid_block.base.fixed_static_x_offset = kid_fixed_cb_x_offset;
+ }
+
+ {
+ let child_base = flow::mut_base(kid);
+ // Left margin edge of kid flow is at our left content edge
+ child_base.position.origin.x = left_content_edge;
+ // Width of kid flow is our content width
+ child_base.position.size.width = content_width;
+ child_base.flags_info.flags.set_inorder(has_inorder_children);
+
+ if !child_base.flags_info.flags.inorder() {
+ child_base.floats = Floats::new();
+ }
+ }
+
+ // Handle tables.
+ match opt_col_widths {
+ Some(ref col_widths) => {
+ // If kid is table_rowgroup or table_row, the column widths info should be
+ // copied from its parent.
+ let kid_width;
+ if kid.is_table() || kid.is_table_rowgroup() || kid.is_table_row() {
+ *kid.col_widths() = col_widths.clone();
+
+ // Width of kid flow is our content width.
+ kid_width = content_width
+ } else if kid.is_table_cell() {
+ // If kid is table_cell, the x offset and width for each cell should be
+ // calculated from parent's column widths info.
+ kid_left_margin_edge = if i == 0 {
+ Au(0)
+ } else {
+ kid_left_margin_edge + col_widths[i-1]
+ };
+
+ kid_width = col_widths[i]
+ } else {
+ // Width of kid flow is our content width.
+ kid_width = content_width
+ }
+
+ let kid_base = flow::mut_base(kid);
+ kid_base.position.origin.x = kid_left_margin_edge;
+ kid_base.position.size.width = kid_width;
+ }
+ None => {}
+ }
+
+ // Per CSS 2.1 § 16.3.1, text decoration propagates to all children in flow.
+ //
+ // TODO(#2018, pcwalton): Do this in the cascade instead.
+ let child_base = flow::mut_base(kid);
+ child_base.flags_info.propagate_text_decoration_from_parent(&flags_info);
+ child_base.flags_info.propagate_text_alignment_from_parent(&flags_info)
+ }
+ }
}
impl Flow for BlockFlow {
@@ -1441,20 +1543,22 @@ impl Flow for BlockFlow {
min/pref widths based on child context widths and dimensions of
any boxes it is responsible for flowing. */
- /* TODO: absolute contexts */
/* TODO: inline-blocks */
fn bubble_widths(&mut self, _: &mut LayoutContext) {
- let mut min_width = Au::new(0);
- let mut pref_width = Au::new(0);
let mut num_floats = 0;
- /* find max width from child block contexts */
+ // Find the maximum width from children.
+ let mut intrinsic_widths = IntrinsicWidths::new();
for child_ctx in self.base.child_iter() {
assert!(child_ctx.is_block_flow() || child_ctx.is_inline_flow() || child_ctx.is_table_kind());
let child_base = flow::mut_base(child_ctx);
- min_width = geometry::max(min_width, child_base.min_width);
- pref_width = geometry::max(pref_width, child_base.pref_width);
+ intrinsic_widths.minimum_width =
+ geometry::max(intrinsic_widths.minimum_width,
+ child_base.intrinsic_widths.total_minimum_width());
+ intrinsic_widths.preferred_width =
+ geometry::max(intrinsic_widths.preferred_width,
+ child_base.intrinsic_widths.total_preferred_width());
num_floats = num_floats + child_base.num_floats;
}
@@ -1465,21 +1569,22 @@ impl Flow for BlockFlow {
self.base.num_floats = num_floats;
}
- /* if not an anonymous block context, add in block box's widths.
- these widths will not include child elements, just padding etc. */
+ // Add in borders, padding, and margins.
for box_ in self.box_.iter() {
{
// Can compute border width here since it doesn't depend on anything.
box_.compute_borders(box_.style())
}
- let (this_minimum_width, this_preferred_width) = box_.minimum_and_preferred_widths();
- min_width = min_width + this_minimum_width;
- pref_width = pref_width + this_preferred_width;
+ let box_intrinsic_widths = box_.intrinsic_widths();
+ intrinsic_widths.minimum_width = geometry::max(intrinsic_widths.minimum_width,
+ box_intrinsic_widths.minimum_width);
+ intrinsic_widths.preferred_width = geometry::max(intrinsic_widths.preferred_width,
+ box_intrinsic_widths.preferred_width);
+ intrinsic_widths.surround_width = box_intrinsic_widths.surround_width
}
- self.base.min_width = min_width;
- self.base.pref_width = pref_width;
+ self.base.intrinsic_widths = intrinsic_widths
}
/// Recursively (top-down) determines the actual width of child contexts and boxes. When called
@@ -1514,6 +1619,10 @@ impl Flow for BlockFlow {
self.compute_used_width(ctx, containing_block_width);
for box_ in self.box_.iter() {
+ // Assign `clear` now so that the assign-heights pass will have the correct value for
+ // it.
+ self.base.clear = box_.style().Box.get().clear;
+
// Move in from the left border edge
left_content_edge = box_.border_box.get().origin.x
+ box_.padding.get().left + box_.border.get().left;
@@ -1539,7 +1648,7 @@ impl Flow for BlockFlow {
self.assign_height_float_inorder();
} else {
debug!("assign_height_inorder: assigning height for block");
- self.assign_height_block_base(ctx, true);
+ self.assign_height_block_base(ctx, true, MarginsMayCollapse);
}
}
@@ -1560,51 +1669,10 @@ impl Flow for BlockFlow {
self.assign_height_inorder(ctx);
return;
}
- self.assign_height_block_base(ctx, false);
+ self.assign_height_block_base(ctx, false, MarginsMayCollapse);
}
}
- // CSS Section 8.3.1 - Collapsing Margins
- // `self`: the Flow whose margins we want to collapse.
- // `collapsing`: value to be set by this function. This tells us how much
- // of the top margin has collapsed with a previous margin.
- // `collapsible`: Potential collapsible margin at the bottom of this flow's box.
- fn collapse_margins(&mut self,
- top_margin_collapsible: bool,
- first_in_flow: &mut bool,
- margin_top: &mut Au,
- top_offset: &mut Au,
- collapsing: &mut Au,
- collapsible: &mut Au) {
- if self.is_float() {
- // Margins between a floated box and any other box do not collapse.
- *collapsing = Au::new(0);
- return;
- }
-
- for box_ in self.box_.iter() {
- // The top margin collapses with its first in-flow block-level child's
- // top margin if the parent has no top border, no top padding.
- if *first_in_flow && top_margin_collapsible {
- // If top-margin of parent is less than top-margin of its first child,
- // the parent box goes down until its top is aligned with the child.
- if *margin_top < box_.margin.get().top {
- // TODO: The position of child floats should be updated and this
- // would influence clearance as well. See #725
- let extra_margin = box_.margin.get().top - *margin_top;
- *top_offset = *top_offset + extra_margin;
- *margin_top = box_.margin.get().top;
- }
- }
- // The bottom margin of an in-flow block-level element collapses
- // with the top margin of its next in-flow block-level sibling.
- *collapsing = geometry::min(box_.margin.get().top, *collapsible);
- *collapsible = box_.margin.get().bottom;
- }
-
- *first_in_flow = false;
- }
-
fn mark_as_root(&mut self) {
self.is_root = true
}
@@ -1663,6 +1731,18 @@ impl Flow for BlockFlow {
}
}
+ fn layer_id(&self, fragment_index: uint) -> LayerId {
+ // FIXME(#2010, pcwalton): This is a hack and is totally bogus in the presence of pseudo-
+ // elements. But until we have incremental reflow we can't do better--we recreate the flow
+ // for every DOM node so otherwise we nuke layers on every reflow.
+ match self.box_ {
+ Some(ref box_) => {
+ LayerId(box_.node.id(), fragment_index)
+ }
+ None => fail!("can't make a layer ID for a flow with no box"),
+ }
+ }
+
fn debug_str(&self) -> ~str {
let txt = if self.is_float() {
~"FloatFlow: "
@@ -1676,6 +1756,10 @@ impl Flow for BlockFlow {
None => ~"",
})
}
+
+ fn is_absolute_containing_block(&self) -> bool {
+ self.is_positioned()
+ }
}
/// The inputs for the widths-and-margins constraint equation.
diff --git a/src/components/main/layout/box_.rs b/src/components/main/layout/box_.rs
index 972ac79da9a..f5acd633944 100644
--- a/src/components/main/layout/box_.rs
+++ b/src/components/main/layout/box_.rs
@@ -4,17 +4,29 @@
//! The `Box` type, which represents the leaves of the layout tree.
+use css::node_style::StyledNode;
+use layout::construct::FlowConstructor;
+use layout::context::LayoutContext;
+use layout::display_list_builder::{DisplayListBuilder, DisplayListBuildingInfo, ToGfxColor};
+use layout::floats::{ClearBoth, ClearLeft, ClearRight, ClearType};
+use layout::flow::{Flow, FlowFlagsInfo};
+use layout::flow;
+use layout::model::{Auto, IntrinsicWidths, MaybeAuto, Specified, specified};
+use layout::model;
+use layout::util::OpaqueNodeMethods;
+use layout::wrapper::{TLayoutNode, ThreadSafeLayoutNode};
+
use extra::url::Url;
use sync::{MutexArc, Arc};
use geom::{Point2D, Rect, Size2D, SideOffsets2D};
use geom::approxeq::ApproxEq;
use gfx::color::rgb;
-use gfx::display_list::{BaseDisplayItem, BorderDisplayItem, BorderDisplayItemClass};
-use gfx::display_list::{LineDisplayItem, LineDisplayItemClass};
-use gfx::display_list::{ImageDisplayItem, ImageDisplayItemClass};
-use gfx::display_list::{SolidColorDisplayItem, SolidColorDisplayItemClass, TextDisplayItem};
-use gfx::display_list::{TextDisplayItemClass, TextDisplayItemFlags, ClipDisplayItem};
-use gfx::display_list::{ClipDisplayItemClass, DisplayListCollection};
+use gfx::display_list::{BackgroundAndBorderLevel, BaseDisplayItem, BorderDisplayItem};
+use gfx::display_list::{BorderDisplayItemClass, ClipDisplayItem, ClipDisplayItemClass};
+use gfx::display_list::{DisplayList, ImageDisplayItem, ImageDisplayItemClass, LineDisplayItem};
+use gfx::display_list::{LineDisplayItemClass, OpaqueNode, SolidColorDisplayItem};
+use gfx::display_list::{SolidColorDisplayItemClass, StackingContext, TextDisplayItem};
+use gfx::display_list::{TextDisplayItemClass, TextDisplayItemFlags};
use gfx::font::FontStyle;
use gfx::text::text_run::TextRun;
use servo_msg::constellation_msg::{ConstellationChan, FrameRectMsg, PipelineId, SubpageId};
@@ -24,26 +36,16 @@ use servo_util::geometry::Au;
use servo_util::geometry;
use servo_util::range::*;
use servo_util::namespace;
+use servo_util::smallvec::{SmallVec, SmallVec0};
use servo_util::str::is_whitespace;
-
use std::cast;
use std::cell::RefCell;
use std::num::Zero;
use style::{ComputedValues, TElement, TNode, cascade, initial_values};
use style::computed_values::{LengthOrPercentage, LengthOrPercentageOrAuto, overflow, LPA_Auto};
-use style::computed_values::{border_style, clear, font_family, line_height, position};
-use style::computed_values::{text_align, text_decoration, vertical_align, visibility, white_space};
-
-use css::node_style::StyledNode;
-use layout::construct::FlowConstructor;
-use layout::context::LayoutContext;
-use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData, ToGfxColor};
-use layout::floats::{ClearBoth, ClearLeft, ClearRight, ClearType};
-use layout::flow::{Flow, FlowFlagsInfo};
-use layout::flow;
-use layout::model::{MaybeAuto, specified, Auto, Specified};
-use layout::util::OpaqueNode;
-use layout::wrapper::{TLayoutNode, ThreadSafeLayoutNode};
+use style::computed_values::{background_attachment, background_repeat, border_style, clear};
+use style::computed_values::{font_family, line_height, position, text_align, text_decoration};
+use style::computed_values::{vertical_align, visibility, white_space};
/// Boxes (`struct Box`) are the leaves of the layout tree. They cannot position themselves. In
/// general, boxes do not have a simple correspondence with CSS boxes in the specification:
@@ -288,18 +290,21 @@ pub enum SplitBoxResult {
}
-/// data for inline boxes
+/// Data for inline boxes.
+///
+/// FIXME(#2013, pcwalton): Copying `InlineParentInfo` vectors all the time is really inefficient.
+/// Use atomic reference counting instead.
#[deriving(Clone)]
pub struct InlineInfo {
- parent_info: ~[InlineParentInfo],
+ parent_info: SmallVec0<InlineParentInfo>,
baseline: Au,
}
impl InlineInfo {
pub fn new() -> InlineInfo {
InlineInfo {
- parent_info: ~[],
- baseline: Au::new(0),
+ parent_info: SmallVec0::new(),
+ baseline: Au(0),
}
}
}
@@ -414,10 +419,16 @@ def_noncontent_horiz!(left, merge_noncontent_inline_left, clear_noncontent_inl
def_noncontent_horiz!(right, merge_noncontent_inline_right, clear_noncontent_inline_right)
impl Box {
- /// Constructs a new `Box` instance.
+ /// Constructs a new `Box` instance for the given node.
+ ///
+ /// Arguments:
+ ///
+ /// * `constructor`: The flow constructor.
+ ///
+ /// * `node`: The node to create a box for.
pub fn new(constructor: &mut FlowConstructor, node: &ThreadSafeLayoutNode) -> Box {
Box {
- node: OpaqueNode::from_thread_safe_layout_node(node),
+ node: OpaqueNodeMethods::from_thread_safe_layout_node(node),
style: node.style().clone(),
border_box: RefCell::new(Au::zero_rect()),
border: RefCell::new(Zero::zero()),
@@ -433,7 +444,7 @@ impl Box {
/// Constructs a new `Box` instance from a specific info.
pub fn new_from_specific_info(node: &ThreadSafeLayoutNode, specific: SpecificBoxInfo) -> Box {
Box {
- node: OpaqueNode::from_thread_safe_layout_node(node),
+ node: OpaqueNodeMethods::from_thread_safe_layout_node(node),
style: node.style().clone(),
border_box: RefCell::new(Au::zero_rect()),
border: RefCell::new(Zero::zero()),
@@ -460,7 +471,7 @@ impl Box {
let (node_style, _) = cascade(&[], false, Some(node.style().get()),
&initial_values(), None);
Box {
- node: OpaqueNode::from_thread_safe_layout_node(node),
+ node: OpaqueNodeMethods::from_thread_safe_layout_node(node),
style: Arc::new(node_style),
border_box: RefCell::new(Au::zero_rect()),
border: RefCell::new(Zero::zero()),
@@ -589,54 +600,55 @@ impl Box {
}
}
- /// Returns the shared part of the width for computation of minimum and preferred width per
- /// CSS 2.1.
- fn guess_width(&self) -> Au {
- let style = self.style();
- let mut margin_left = Au::new(0);
- let mut margin_right = Au::new(0);
- let mut padding_left = Au::new(0);
- let mut padding_right = Au::new(0);
-
- match self.specific {
- GenericBox | IframeBox(_) | ImageBox(_) => {
- margin_left = MaybeAuto::from_style(style.Margin.get().margin_left,
- Au::new(0)).specified_or_zero();
- margin_right = MaybeAuto::from_style(style.Margin.get().margin_right,
- Au::new(0)).specified_or_zero();
- padding_left = self.compute_padding_length(style.Padding.get().padding_left,
- Au::new(0));
- padding_right = self.compute_padding_length(style.Padding.get().padding_right,
- Au::new(0));
- }
- TableBox | TableCellBox => {
- padding_left = self.compute_padding_length(style.Padding.get().padding_left,
- Au::new(0));
- padding_right = self.compute_padding_length(style.Padding.get().padding_right,
- Au::new(0));
+ /// Uses the style only to estimate the intrinsic widths. These may be modified for text or
+ /// replaced elements.
+ fn style_specified_intrinsic_width(&self) -> IntrinsicWidths {
+ let (use_margins, use_padding) = match self.specific {
+ GenericBox | IframeBox(_) | ImageBox(_) => (true, true),
+ TableBox | TableCellBox => (false, true),
+ TableWrapperBox => (true, false),
+ TableRowBox => (false, false),
+ ScannedTextBox(_) | TableColumnBox(_) | UnscannedTextBox(_) => {
+ // Styles are irrelevant for these kinds of boxes.
+ return IntrinsicWidths::new()
}
- TableWrapperBox => {
- margin_left = MaybeAuto::from_style(style.Margin.get().margin_left,
- Au::new(0)).specified_or_zero();
- margin_right = MaybeAuto::from_style(style.Margin.get().margin_right,
- Au::new(0)).specified_or_zero();
- }
- TableRowBox => {}
- ScannedTextBox(_) | TableColumnBox(_) | UnscannedTextBox(_) => return Au(0),
- }
+ };
+ let style = self.style();
let width = MaybeAuto::from_style(style.Box.get().width, Au::new(0)).specified_or_zero();
- width + margin_left + margin_right + padding_left + padding_right +
- self.border.get().left + self.border.get().right
+ let (margin_left, margin_right) = if use_margins {
+ (MaybeAuto::from_style(style.Margin.get().margin_left, Au(0)).specified_or_zero(),
+ MaybeAuto::from_style(style.Margin.get().margin_right, Au(0)).specified_or_zero())
+ } else {
+ (Au(0), Au(0))
+ };
+
+ let (padding_left, padding_right) = if use_padding {
+ (self.compute_padding_length(style.Padding.get().padding_left, Au(0)),
+ self.compute_padding_length(style.Padding.get().padding_right, Au(0)))
+ } else {
+ (Au(0), Au(0))
+ };
+
+ let surround_width = margin_left + margin_right + padding_left + padding_right +
+ self.border.get().left + self.border.get().right;
+
+ IntrinsicWidths {
+ minimum_width: width,
+ preferred_width: width,
+ surround_width: surround_width,
+ }
}
pub fn calculate_line_height(&self, font_size: Au) -> Au {
- match self.line_height() {
+ let from_inline = match self.style().InheritedBox.get().line_height {
line_height::Normal => font_size.scale_by(1.14),
line_height::Number(l) => font_size.scale_by(l),
line_height::Length(l) => l
- }
+ };
+ let minimum = self.style().InheritedBox.get()._servo_minimum_line_height;
+ Au::max(from_inline, minimum)
}
/// Populates the box model border parameters from the given computed style.
@@ -864,10 +876,6 @@ impl Box {
self.style().InheritedText.get().text_align
}
- pub fn line_height(&self) -> line_height::T {
- self.style().InheritedBox.get().line_height
- }
-
pub fn vertical_align(&self) -> vertical_align::T {
self.style().Box.get().vertical_align
}
@@ -920,15 +928,6 @@ impl Box {
}
}
- /// Returns true if this element is replaced content. This is true for images, form elements,
- /// and so on.
- pub fn is_replaced(&self) -> bool {
- match self.specific {
- ImageBox(..) => true,
- _ => false,
- }
- }
-
/// Returns true if this element can be split. This is true for text boxes.
pub fn can_split(&self) -> bool {
match self.specific {
@@ -951,12 +950,10 @@ impl Box {
(Au::new(0), Au::new(0))
}
- pub fn paint_inline_background_border_if_applicable<E:ExtraDisplayListData>(
- &self,
- index: uint,
- lists: &RefCell<DisplayListCollection<E>>,
- absolute_bounds: &Rect<Au>,
- offset: &Point2D<Au>) {
+ pub fn paint_inline_background_border_if_applicable(&self,
+ list: &mut DisplayList,
+ absolute_bounds: &Rect<Au>,
+ offset: &Point2D<Au>) {
// FIXME: This causes a lot of background colors to be displayed when they are clearly not
// needed. We could use display list optimization to clean this up, but it still seems
// inefficient. What we really want is something like "nearest ancestor element that
@@ -965,7 +962,7 @@ impl Box {
match info.get() {
&Some(ref box_info) => {
let mut bg_rect = absolute_bounds.clone();
- for info in box_info.parent_info.rev_iter() {
+ for info in box_info.parent_info.as_slice().rev_iter() {
// TODO (ksh8281) compute vertical-align, line-height
bg_rect.origin.y = box_info.baseline + offset.y - info.font_ascent;
bg_rect.size.height = info.font_ascent + info.font_descent;
@@ -973,23 +970,24 @@ impl Box {
info.style.get().Background.get().background_color);
if !background_color.alpha.approx_eq(&0.0) {
- lists.with_mut(|lists| {
- let solid_color_display_item = ~SolidColorDisplayItem {
- base: BaseDisplayItem {
- bounds: bg_rect.clone(),
- extra: ExtraDisplayListData::new(self),
- },
- color: background_color.to_gfx_color(),
- };
-
- lists.lists[index].append_item(SolidColorDisplayItemClass(solid_color_display_item))
- });
+ let solid_color_display_item = ~SolidColorDisplayItem {
+ base: BaseDisplayItem {
+ bounds: bg_rect.clone(),
+ node: self.node,
+ },
+ color: background_color.to_gfx_color(),
+ };
+
+ list.push(SolidColorDisplayItemClass(solid_color_display_item))
}
+
let border = &info.border;
+
// Fast path.
if border.is_zero() {
- continue;
+ continue
}
+
bg_rect.origin.y = bg_rect.origin.y - border.top;
bg_rect.size.height = bg_rect.size.height + border.top + border.bottom;
@@ -1003,26 +1001,20 @@ impl Box {
let bottom_style = style.Border.get().border_bottom_style;
let left_style = style.Border.get().border_left_style;
+ let border_display_item = ~BorderDisplayItem {
+ base: BaseDisplayItem {
+ bounds: bg_rect,
+ node: self.node,
+ },
+ border: border.clone(),
+ color: SideOffsets2D::new(top_color.to_gfx_color(),
+ right_color.to_gfx_color(),
+ bottom_color.to_gfx_color(),
+ left_color.to_gfx_color()),
+ style: SideOffsets2D::new(top_style, right_style, bottom_style, left_style)
+ };
- lists.with_mut(|lists| {
- let border_display_item = ~BorderDisplayItem {
- base: BaseDisplayItem {
- bounds: bg_rect,
- extra: ExtraDisplayListData::new(self),
- },
- border: border.clone(),
- color: SideOffsets2D::new(top_color.to_gfx_color(),
- right_color.to_gfx_color(),
- bottom_color.to_gfx_color(),
- left_color.to_gfx_color()),
- style: SideOffsets2D::new(top_style,
- right_style,
- bottom_style,
- left_style)
- };
-
- lists.lists[index].append_item(BorderDisplayItemClass(border_display_item))
- });
+ list.push(BorderDisplayItemClass(border_display_item));
bg_rect.origin.x = bg_rect.origin.x + border.left;
bg_rect.size.width = bg_rect.size.width - border.left - border.right;
@@ -1033,11 +1025,9 @@ impl Box {
}
/// Adds the display items necessary to paint the background of this box to the display list if
/// necessary.
- pub fn paint_background_if_applicable<E:ExtraDisplayListData>(
- &self,
+ pub fn paint_background_if_applicable(&self,
+ list: &mut DisplayList,
builder: &DisplayListBuilder,
- index: uint,
- lists: &RefCell<DisplayListCollection<E>>,
absolute_bounds: &Rect<Au>) {
// FIXME: This causes a lot of background colors to be displayed when they are clearly not
// needed. We could use display list optimization to clean this up, but it still seems
@@ -1046,17 +1036,15 @@ impl Box {
let style = self.style();
let background_color = style.resolve_color(style.Background.get().background_color);
if !background_color.alpha.approx_eq(&0.0) {
- lists.with_mut(|lists| {
- let solid_color_display_item = ~SolidColorDisplayItem {
- base: BaseDisplayItem {
- bounds: *absolute_bounds,
- extra: ExtraDisplayListData::new(self),
- },
- color: background_color.to_gfx_color(),
- };
+ let display_item = ~SolidColorDisplayItem {
+ base: BaseDisplayItem {
+ bounds: *absolute_bounds,
+ node: self.node,
+ },
+ color: background_color.to_gfx_color(),
+ };
- lists.lists[index].append_item(SolidColorDisplayItemClass(solid_color_display_item))
- });
+ list.push(SolidColorDisplayItemClass(display_item))
}
// The background image is painted on top of the background color.
@@ -1064,22 +1052,81 @@ impl Box {
// http://www.w3.org/TR/CSS21/colors.html#background
match style.Background.get().background_image {
Some(ref image_url) => {
- let mut holder = ImageHolder::new(image_url.clone(), builder.ctx.image_cache.clone());
+ let mut holder = ImageHolder::new(image_url.clone(),
+ builder.ctx.image_cache.clone());
match holder.get_image() {
Some(image) => {
debug!("(building display list) building background image");
- // Place the image into the display list.
- lists.with_mut(|lists| {
- let image_display_item = ~ImageDisplayItem {
- base: BaseDisplayItem {
- bounds: *absolute_bounds,
- extra: ExtraDisplayListData::new(self),
- },
- image: image.clone(),
- };
- lists.lists[index].append_item(ImageDisplayItemClass(image_display_item));
+ // Adjust bounds for `background-position` and `background-attachment`.
+ let mut bounds = *absolute_bounds;
+ let horizontal_position = model::specified(
+ style.Background.get().background_position.horizontal,
+ bounds.size.width);
+ let vertical_position = model::specified(
+ style.Background.get().background_position.vertical,
+ bounds.size.height);
+
+ let clip_display_item;
+ match style.Background.get().background_attachment {
+ background_attachment::scroll => {
+ clip_display_item = None;
+ bounds.origin.x = bounds.origin.x + horizontal_position;
+ bounds.origin.y = bounds.origin.y + vertical_position;
+ bounds.size.width = bounds.size.width - horizontal_position;
+ bounds.size.height = bounds.size.height - vertical_position;
+ }
+ background_attachment::fixed => {
+ clip_display_item = Some(~ClipDisplayItem {
+ base: BaseDisplayItem {
+ bounds: bounds,
+ node: self.node,
+ },
+ child_list: SmallVec0::new(),
+ need_clip: true,
+ });
+
+ bounds = Rect {
+ origin: Point2D(horizontal_position, vertical_position),
+ size: Size2D(bounds.origin.x + bounds.size.width,
+ bounds.origin.y + bounds.size.height),
+ }
+ }
+ }
+ // Adjust sizes for `background-repeat`.
+ match style.Background.get().background_repeat {
+ background_repeat::no_repeat => {
+ bounds.size.width = Au::from_px(image.get().width as int);
+ bounds.size.height = Au::from_px(image.get().height as int)
+ }
+ background_repeat::repeat_x => {
+ bounds.size.height = Au::from_px(image.get().height as int)
+ }
+ background_repeat::repeat_y => {
+ bounds.size.width = Au::from_px(image.get().width as int)
+ }
+ background_repeat::repeat => {}
+ };
+
+
+ // Create the image display item.
+ let image_display_item = ImageDisplayItemClass(~ImageDisplayItem {
+ base: BaseDisplayItem {
+ bounds: bounds,
+ node: self.node,
+ },
+ image: image.clone(),
+ stretch_size: Size2D(Au::from_px(image.get().width as int),
+ Au::from_px(image.get().height as int)),
});
+
+ match clip_display_item {
+ None => list.push(image_display_item),
+ Some(mut clip_display_item) => {
+ clip_display_item.child_list.push(image_display_item);
+ list.push(ClipDisplayItemClass(clip_display_item))
+ }
+ }
}
None => {
// No image data at all? Do nothing.
@@ -1095,11 +1142,7 @@ impl Box {
/// Adds the display items necessary to paint the borders of this box to a display list if
/// necessary.
- pub fn paint_borders_if_applicable<E:ExtraDisplayListData>(
- &self,
- index: uint,
- lists: &RefCell<DisplayListCollection<E>>,
- abs_bounds: &Rect<Au>) {
+ pub fn paint_borders_if_applicable(&self, list: &mut DisplayList, abs_bounds: &Rect<Au>) {
// Fast path.
let border = self.border.get();
if border.is_zero() {
@@ -1122,75 +1165,142 @@ impl Box {
- self.noncontent_inline_right();
// Append the border to the display list.
- lists.with_mut(|lists| {
- let border_display_item = ~BorderDisplayItem {
- base: BaseDisplayItem {
- bounds: abs_bounds,
- extra: ExtraDisplayListData::new(self),
- },
- border: border,
- color: SideOffsets2D::new(top_color.to_gfx_color(),
- right_color.to_gfx_color(),
- bottom_color.to_gfx_color(),
- left_color.to_gfx_color()),
- style: SideOffsets2D::new(top_style,
- right_style,
- bottom_style,
- left_style)
- };
+ let border_display_item = ~BorderDisplayItem {
+ base: BaseDisplayItem {
+ bounds: abs_bounds,
+ node: self.node,
+ },
+ border: border,
+ color: SideOffsets2D::new(top_color.to_gfx_color(),
+ right_color.to_gfx_color(),
+ bottom_color.to_gfx_color(),
+ left_color.to_gfx_color()),
+ style: SideOffsets2D::new(top_style,
+ right_style,
+ bottom_style,
+ left_style)
+ };
- lists.lists[index].append_item(BorderDisplayItemClass(border_display_item))
- });
+ list.push(BorderDisplayItemClass(border_display_item))
+ }
+
+ fn build_debug_borders_around_text_boxes(&self,
+ stacking_context: &mut StackingContext,
+ flow_origin: Point2D<Au>,
+ text_box: &ScannedTextBoxInfo) {
+ let box_bounds = self.border_box.get();
+ let absolute_box_bounds = box_bounds.translate(&flow_origin);
+
+ // Compute the text box bounds and draw a border surrounding them.
+ let debug_border = SideOffsets2D::new_all_same(Au::from_px(1));
+
+ let border_display_item = ~BorderDisplayItem {
+ base: BaseDisplayItem {
+ bounds: absolute_box_bounds,
+ node: self.node,
+ },
+ border: debug_border,
+ color: SideOffsets2D::new_all_same(rgb(0, 0, 200)),
+ style: SideOffsets2D::new_all_same(border_style::solid)
+
+ };
+ stacking_context.content.push(BorderDisplayItemClass(border_display_item));
+
+ // Draw a rectangle representing the baselines.
+ let ascent = text_box.run.get().metrics_for_range(&text_box.range).ascent;
+ let baseline = Rect(absolute_box_bounds.origin + Point2D(Au(0), ascent),
+ Size2D(absolute_box_bounds.size.width, Au(0)));
+
+ let line_display_item = ~LineDisplayItem {
+ base: BaseDisplayItem {
+ bounds: baseline,
+ node: self.node,
+ },
+ color: rgb(0, 200, 0),
+ style: border_style::dashed,
+
+ };
+ stacking_context.content.push(LineDisplayItemClass(line_display_item))
}
- /// Adds the display items for this box to the given display list.
+ fn build_debug_borders_around_box(&self,
+ stacking_context: &mut StackingContext,
+ flow_origin: Point2D<Au>) {
+ let box_bounds = self.border_box.get();
+ let absolute_box_bounds = box_bounds.translate(&flow_origin);
+
+ // This prints a debug border around the border of this box.
+ let debug_border = SideOffsets2D::new_all_same(Au::from_px(1));
+
+ let border_display_item = ~BorderDisplayItem {
+ base: BaseDisplayItem {
+ bounds: absolute_box_bounds,
+ node: self.node,
+ },
+ border: debug_border,
+ color: SideOffsets2D::new_all_same(rgb(0, 0, 200)),
+ style: SideOffsets2D::new_all_same(border_style::solid)
+
+ };
+ stacking_context.content.push(BorderDisplayItemClass(border_display_item))
+ }
+
+ /// Adds the display items for this box to the given stacking context.
///
/// Arguments:
+ ///
+ /// * `stacking_context`: The stacking context to add display items to.
/// * `builder`: The display list builder, which manages the coordinate system and options.
/// * `dirty`: The dirty rectangle in the coordinate system of the owning flow.
/// * `flow_origin`: Position of the origin of the owning flow wrt the display list root flow.
/// box.
- /// * `list`: The display list to which items should be appended.
- ///
- /// TODO: To implement stacking contexts correctly, we need to create a set of display lists,
- /// one per layer of the stacking context (CSS 2.1 § 9.9.1). Each box is passed the list set
- /// 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.
- pub fn build_display_list<E:ExtraDisplayListData>(
- &self,
+ /// * `flow`: The flow that this box belongs to.
+ pub fn build_display_list(&self,
+ stacking_context: &mut StackingContext,
builder: &DisplayListBuilder,
- dirty: &Rect<Au>,
+ _: &DisplayListBuildingInfo,
flow_origin: Point2D<Au>,
flow: &Flow,
- index: uint,
- lists: &RefCell<DisplayListCollection<E>>) {
+ background_and_border_level: BackgroundAndBorderLevel) {
// Box position wrt to the owning flow.
let box_bounds = self.border_box.get();
let absolute_box_bounds = box_bounds.translate(&flow_origin);
debug!("Box::build_display_list at rel={}, abs={}: {:s}",
- box_bounds, absolute_box_bounds, self.debug_str());
- debug!("Box::build_display_list: dirty={}, flow_origin={}", *dirty, flow_origin);
+ box_bounds,
+ absolute_box_bounds,
+ self.debug_str());
+ debug!("Box::build_display_list: dirty={}, flow_origin={}", builder.dirty, flow_origin);
if self.style().InheritedBox.get().visibility != visibility::visible {
- return;
+ return
}
- if absolute_box_bounds.intersects(dirty) {
- debug!("Box::build_display_list: intersected. Adding display item...");
- } else {
+ if !absolute_box_bounds.intersects(&builder.dirty) {
debug!("Box::build_display_list: Did not intersect...");
- return;
+ return
}
- self.paint_inline_background_border_if_applicable(index, lists, &absolute_box_bounds, &flow_origin);
- // Add the background to the list, if applicable.
- self.paint_background_if_applicable(builder, index, lists, &absolute_box_bounds);
+ debug!("Box::build_display_list: intersected. Adding display item...");
- // Add a border, if applicable.
- //
- // TODO: Outlines.
- self.paint_borders_if_applicable(index, lists, &absolute_box_bounds);
+ {
+ let list =
+ stacking_context.list_for_background_and_border_level(background_and_border_level);
+
+ // Add a background to the list, if this is an inline.
+ //
+ // FIXME(pcwalton): This is kind of ugly; merge with the call below?
+ self.paint_inline_background_border_if_applicable(list,
+ &absolute_box_bounds,
+ &flow_origin);
+
+ // Add the background to the list, if applicable.
+ self.paint_background_if_applicable(list, builder, &absolute_box_bounds);
+
+ // Add a border, if applicable.
+ //
+ // TODO: Outlines.
+ self.paint_borders_if_applicable(list, &absolute_box_bounds);
+ }
match self.specific {
UnscannedTextBox(_) => fail!("Shouldn't see unscanned boxes here."),
@@ -1204,7 +1314,7 @@ impl Box {
let inline_info = self.inline_info.borrow();
match inline_info.get() {
&Some(ref info) => {
- for data in info.parent_info.rev_iter() {
+ for data in info.parent_info.as_slice().rev_iter() {
let parent_info = FlowFlagsInfo::new(data.style.get());
flow_flags.propagate_text_decoration_from_parent(&parent_info);
}
@@ -1224,100 +1334,45 @@ impl Box {
- self.noncontent_inline_right();
// Create the text box.
- lists.with_mut(|lists| {
- let text_display_item = ~TextDisplayItem {
- base: BaseDisplayItem {
- bounds: bounds,
- extra: ExtraDisplayListData::new(self),
- },
- text_run: text_box.run.clone(),
- range: text_box.range,
- text_color: text_color,
- overline_color: flow_flags.overline_color(text_color),
- underline_color: flow_flags.underline_color(text_color),
- line_through_color: flow_flags.line_through_color(text_color),
- flags: text_flags,
- };
+ let text_display_item = ~TextDisplayItem {
+ base: BaseDisplayItem {
+ bounds: bounds,
+ node: self.node,
+ },
+ text_run: text_box.run.clone(),
+ range: text_box.range,
+ text_color: text_color,
+ overline_color: flow_flags.overline_color(text_color),
+ underline_color: flow_flags.underline_color(text_color),
+ line_through_color: flow_flags.line_through_color(text_color),
+ flags: text_flags,
+ };
- lists.lists[index].append_item(TextDisplayItemClass(text_display_item));
- });
+ stacking_context.content.push(TextDisplayItemClass(text_display_item));
// Draw debug frames for text bounds.
//
// FIXME(pcwalton): This is a bit of an abuse of the logging infrastructure. We
// should have a real `SERVO_DEBUG` system.
- debug!("{:?}", {
- // Compute the text box bounds and draw a border surrounding them.
- let debug_border = SideOffsets2D::new_all_same(Au::from_px(1));
-
- lists.with_mut(|lists| {
- let border_display_item = ~BorderDisplayItem {
- base: BaseDisplayItem {
- bounds: absolute_box_bounds,
- extra: ExtraDisplayListData::new(self),
- },
- border: debug_border,
- color: SideOffsets2D::new_all_same(rgb(0, 0, 200)),
- style: SideOffsets2D::new_all_same(border_style::solid)
-
- };
- lists.lists[index].append_item(BorderDisplayItemClass(border_display_item));
- });
-
- // Draw a rectangle representing the baselines.
- let ascent = text_box.run.get().metrics_for_range(
- &text_box.range).ascent;
- let baseline = Rect(absolute_box_bounds.origin + Point2D(Au(0), ascent),
- Size2D(absolute_box_bounds.size.width, Au(0)));
-
- lists.with_mut(|lists| {
- let line_display_item = ~LineDisplayItem {
- base: BaseDisplayItem {
- bounds: baseline,
- extra: ExtraDisplayListData::new(self),
- },
- color: rgb(0, 200, 0),
- style: border_style::dashed
-
- };
- lists.lists[index].append_item(LineDisplayItemClass(line_display_item));
- });
- });
+ debug!("{:?}", self.build_debug_borders_around_text_boxes(stacking_context,
+ flow_origin,
+ text_box))
},
GenericBox | IframeBox(..) | TableBox | TableCellBox | TableRowBox |
TableWrapperBox => {
- lists.with_mut(|lists| {
- let item = ~ClipDisplayItem {
- base: BaseDisplayItem {
- bounds: absolute_box_bounds,
- extra: ExtraDisplayListData::new(self),
- },
- child_list: ~[],
- need_clip: self.needs_clip()
- };
- lists.lists[index].append_item(ClipDisplayItemClass(item));
- });
+ let item = ~ClipDisplayItem {
+ base: BaseDisplayItem {
+ bounds: absolute_box_bounds,
+ node: self.node,
+ },
+ child_list: SmallVec0::new(),
+ need_clip: self.needs_clip()
+ };
+ stacking_context.content.push(ClipDisplayItemClass(item));
// FIXME(pcwalton): This is a bit of an abuse of the logging infrastructure. We
// should have a real `SERVO_DEBUG` system.
- debug!("{:?}", {
- // This prints a debug border around the border of this box.
- let debug_border = SideOffsets2D::new_all_same(Au::from_px(1));
-
- lists.with_mut(|lists| {
- let border_display_item = ~BorderDisplayItem {
- base: BaseDisplayItem {
- bounds: absolute_box_bounds,
- extra: ExtraDisplayListData::new(self),
- },
- border: debug_border,
- color: SideOffsets2D::new_all_same(rgb(0, 0, 200)),
- style: SideOffsets2D::new_all_same(border_style::solid)
-
- };
- lists.lists[index].append_item(BorderDisplayItemClass(border_display_item));
- });
- });
+ debug!("{:?}", self.build_debug_borders_around_box(stacking_context, flow_origin))
},
ImageBox(ref image_box) => {
let mut image_ref = image_box.image.borrow_mut();
@@ -1335,16 +1390,15 @@ impl Box {
debug!("(building display list) building image box");
// Place the image into the display list.
- lists.with_mut(|lists| {
- let image_display_item = ~ImageDisplayItem {
- base: BaseDisplayItem {
- bounds: bounds,
- extra: ExtraDisplayListData::new(self),
- },
- image: image.clone(),
- };
- lists.lists[index].append_item(ImageDisplayItemClass(image_display_item));
- });
+ let image_display_item = ~ImageDisplayItem {
+ base: BaseDisplayItem {
+ bounds: bounds,
+ node: self.node,
+ },
+ image: image.clone(),
+ stretch_size: bounds.size,
+ };
+ stacking_context.content.push(ImageDisplayItemClass(image_display_item))
}
None => {
// No image data at all? Do nothing.
@@ -1355,24 +1409,7 @@ impl Box {
}
// FIXME(pcwalton): This is a bit of an abuse of the logging infrastructure. We
// should have a real `SERVO_DEBUG` system.
- debug!("{:?}", {
- let debug_border = SideOffsets2D::new_all_same(Au::from_px(1));
-
- lists.with_mut(|lists| {
- let border_display_item = ~BorderDisplayItem {
- base: BaseDisplayItem {
- bounds: absolute_box_bounds,
- extra: ExtraDisplayListData::new(self),
- },
- border: debug_border,
- color: SideOffsets2D::new_all_same(rgb(0, 0, 200)),
- style: SideOffsets2D::new_all_same(border_style::solid)
-
- };
- lists.lists[index].append_item(BorderDisplayItemClass(border_display_item))
- });
- });
-
+ debug!("{:?}", self.build_debug_borders_around_box(stacking_context, flow_origin))
}
}
@@ -1392,18 +1429,19 @@ impl Box {
}
_ => {}
}
-
}
- /// Returns the *minimum width* and *preferred width* of this box as defined by CSS 2.1.
- pub fn minimum_and_preferred_widths(&self) -> (Au, Au) {
- let guessed_width = self.guess_width();
- let (additional_minimum, additional_preferred) = match self.specific {
- GenericBox | IframeBox(_) | TableBox | TableCellBox | TableColumnBox(_) |
- TableRowBox | TableWrapperBox => (Au(0), Au(0)),
+ /// Returns the intrinsic widths of this fragment.
+ pub fn intrinsic_widths(&self) -> IntrinsicWidths {
+ let mut result = self.style_specified_intrinsic_width();
+
+ match self.specific {
+ GenericBox | IframeBox(_) | TableBox | TableCellBox | TableColumnBox(_) | TableRowBox |
+ TableWrapperBox => {}
ImageBox(ref image_box_info) => {
let image_width = image_box_info.image_width();
- (image_width, image_width)
+ result.minimum_width = geometry::max(result.minimum_width, image_width);
+ result.preferred_width = geometry::max(result.preferred_width, image_width);
}
ScannedTextBox(ref text_box_info) => {
let range = &text_box_info.range;
@@ -1415,11 +1453,29 @@ impl Box {
max_line_width = Au::max(max_line_width, line_metrics.advance_width);
}
- (min_line_width, max_line_width)
+ result.minimum_width = geometry::max(result.minimum_width, min_line_width);
+ result.preferred_width = geometry::max(result.preferred_width, max_line_width);
}
UnscannedTextBox(..) => fail!("Unscanned text boxes should have been scanned by now!"),
- };
- (guessed_width + additional_minimum, guessed_width + additional_preferred)
+ }
+
+ // Take borders and padding for parent inline boxes into account.
+ let inline_info = self.inline_info.get();
+ match inline_info {
+ None => {}
+ Some(ref inline_info) => {
+ for inline_parent_info in inline_info.parent_info.iter() {
+ let border_width = inline_parent_info.border.left +
+ inline_parent_info.border.right;
+ let padding_width = inline_parent_info.padding.left +
+ inline_parent_info.padding.right;
+ result.minimum_width = result.minimum_width + border_width + padding_width;
+ result.preferred_width = result.preferred_width + border_width + padding_width;
+ }
+ }
+ }
+
+ result
}
@@ -1440,8 +1496,8 @@ impl Box {
UnscannedTextBox(_) => fail!("Unscanned text boxes should have been scanned by now!"),
}
}
+
/// Returns, and computes, the height of this box.
- ///
pub fn content_height(&self) -> Au {
match self.specific {
GenericBox | IframeBox(_) | TableBox | TableCellBox | TableRowBox |
@@ -1625,16 +1681,16 @@ impl Box {
}
}
- /// Assigns replaced width for this box only if it is replaced content.
- ///
- /// This assigns only the width, not margin or anything else.
- /// CSS 2.1 § 10.3.2.
- pub fn assign_replaced_width_if_necessary(&self,container_width: Au) {
+ /// Assigns replaced width, padding, and margins for this box only if it is replaced content
+ /// per CSS 2.1 § 10.3.2.
+ pub fn assign_replaced_width_if_necessary(&self, container_width: Au) {
match self.specific {
GenericBox | IframeBox(_) | TableBox | TableCellBox | TableRowBox |
TableWrapperBox => {}
ImageBox(ref image_box_info) => {
- // TODO(ksh8281): compute border,margin,padding
+ self.compute_padding(self.style(), container_width);
+
+ // TODO(ksh8281): compute border,margin
let width = ImageBoxInfo::style_length(self.style().Box.get().width,
image_box_info.dom_width,
container_width);
diff --git a/src/components/main/layout/construct.rs b/src/components/main/layout/construct.rs
index 8b25937c624..e24805f687a 100644
--- a/src/components/main/layout/construct.rs
+++ b/src/components/main/layout/construct.rs
@@ -22,14 +22,14 @@
use css::node_style::StyledNode;
use layout::block::BlockFlow;
-use layout::box_::{Box, GenericBox, IframeBox, IframeBoxInfo, ImageBox, ImageBoxInfo, TableBox};
-use layout::box_::{TableCellBox, TableColumnBox, TableColumnBoxInfo, TableRowBox, TableWrapperBox};
-use layout::box_::{InlineInfo, InlineParentInfo, SpecificBoxInfo, UnscannedTextBox};
-use layout::box_::{UnscannedTextBoxInfo};
+use layout::box_::{Box, GenericBox, IframeBox, IframeBoxInfo, ImageBox, ImageBoxInfo};
+use layout::box_::{InlineInfo, InlineParentInfo, SpecificBoxInfo};
+use layout::box_::{TableBox, TableCellBox, TableColumnBox, TableColumnBoxInfo, TableRowBox};
+use layout::box_::{TableWrapperBox, UnscannedTextBox, UnscannedTextBoxInfo};
use layout::context::LayoutContext;
use layout::floats::FloatKind;
use layout::flow::{Flow, ImmutableFlowUtils, MutableOwnedFlowUtils};
-use layout::flow::{Descendants, AbsDescendants, FixedDescendants};
+use layout::flow::{Descendants, AbsDescendants};
use layout::flow_list::{Rawlink};
use layout::inline::InlineFlow;
use layout::table_wrapper::TableWrapperFlow;
@@ -40,31 +40,34 @@ use layout::table_rowgroup::TableRowGroupFlow;
use layout::table_row::TableRowFlow;
use layout::table_cell::TableCellFlow;
use layout::text::TextRunScanner;
-use layout::util::{LayoutDataAccess, OpaqueNode};
+use layout::util::{LayoutDataAccess, OpaqueNodeMethods};
use layout::wrapper::{PostorderNodeMutTraversal, TLayoutNode, ThreadSafeLayoutNode};
+use layout::wrapper::{Before, BeforeBlock, After, AfterBlock, Normal};
+use extra::url::Url;
+use gfx::display_list::OpaqueNode;
use gfx::font_context::FontContext;
use script::dom::bindings::codegen::InheritTypes::TextCast;
use script::dom::bindings::js::JS;
-use script::dom::element::{HTMLIFrameElementTypeId, HTMLImageElementTypeId, HTMLObjectElementTypeId};
-use script::dom::element::{HTMLTableElementTypeId, HTMLTableSectionElementTypeId};
-use script::dom::element::{HTMLTableDataCellElementTypeId, HTMLTableHeaderCellElementTypeId};
-use script::dom::element::{HTMLTableColElementTypeId, HTMLTableRowElementTypeId};
+use script::dom::element::{HTMLIFrameElementTypeId, HTMLImageElementTypeId};
+use script::dom::element::{HTMLObjectElementTypeId};
+use script::dom::element::{HTMLTableColElementTypeId, HTMLTableDataCellElementTypeId};
+use script::dom::element::{HTMLTableElementTypeId, HTMLTableHeaderCellElementTypeId};
+use script::dom::element::{HTMLTableRowElementTypeId, HTMLTableSectionElementTypeId};
use script::dom::node::{CommentNodeTypeId, DoctypeNodeTypeId, DocumentFragmentNodeTypeId};
use script::dom::node::{DocumentNodeTypeId, ElementNodeTypeId, ProcessingInstructionNodeTypeId};
use script::dom::node::{TextNodeTypeId};
use script::dom::text::Text;
-use style::computed_values::{display, position, float, white_space};
-use style::ComputedValues;
+use servo_util::geometry::Au;
use servo_util::namespace;
-use servo_util::url::parse_url;
-use servo_util::url::is_image_data;
+use servo_util::smallvec::SmallVec;
use servo_util::str::is_whitespace;
-
-use extra::url::Url;
-use sync::Arc;
+use servo_util::url::{is_image_data, parse_url};
use std::mem;
use std::num::Zero;
+use style::ComputedValues;
+use style::computed_values::{display, position, float, white_space};
+use sync::Arc;
/// The results of flow construction for a DOM node.
pub enum ConstructionResult {
@@ -75,7 +78,7 @@ pub enum ConstructionResult {
/// This node contributed a flow at the proper position in the tree.
/// Nothing more needs to be done for this node. It has bubbled up fixed
/// and absolute descendant flows that have a CB above it.
- FlowConstructionResult(~Flow, AbsDescendants, FixedDescendants),
+ FlowConstructionResult(~Flow, AbsDescendants),
/// This node contributed some object or objects that will be needed to construct a proper flow
/// later up the tree, but these objects have not yet found their home.
@@ -86,7 +89,7 @@ impl ConstructionResult {
fn destroy(&mut self) {
match *self {
NoConstructionResult => {}
- FlowConstructionResult(ref mut flow, _, _) => flow.destroy(),
+ FlowConstructionResult(ref mut flow, _) => flow.destroy(),
ConstructionItemConstructionResult(ref mut item) => item.destroy(),
}
}
@@ -132,9 +135,6 @@ struct InlineBoxesConstructionResult {
/// Any absolute descendants that we're bubbling up.
abs_descendants: AbsDescendants,
-
- /// Any fixed descendants that we're bubbling up.
- fixed_descendants: FixedDescendants,
}
/// Represents an {ib} split that has not yet found the containing block that it belongs to. This
@@ -281,7 +281,8 @@ impl<'a> FlowConstructor<'a> {
}
/// Builds the `ImageBoxInfo` for the given image. This is out of line to guide inlining.
- fn build_box_info_for_image(&mut self, node: &ThreadSafeLayoutNode, url: Option<Url>) -> SpecificBoxInfo {
+ fn build_box_info_for_image(&mut self, node: &ThreadSafeLayoutNode, url: Option<Url>)
+ -> SpecificBoxInfo {
match url {
None => GenericBox,
Some(url) => {
@@ -296,19 +297,25 @@ impl<'a> FlowConstructor<'a> {
pub fn build_specific_box_info_for_node(&mut self, node: &ThreadSafeLayoutNode)
-> SpecificBoxInfo {
match node.type_id() {
- ElementNodeTypeId(HTMLImageElementTypeId) => self.build_box_info_for_image(node, node.image_url()),
- ElementNodeTypeId(HTMLIFrameElementTypeId) => IframeBox(IframeBoxInfo::new(node)),
- ElementNodeTypeId(HTMLObjectElementTypeId) => {
+ Some(ElementNodeTypeId(HTMLImageElementTypeId)) => {
+ self.build_box_info_for_image(node, node.image_url())
+ }
+ Some(ElementNodeTypeId(HTMLIFrameElementTypeId)) => {
+ IframeBox(IframeBoxInfo::new(node))
+ }
+ Some(ElementNodeTypeId(HTMLObjectElementTypeId)) => {
let data = node.get_object_data(&self.layout_context.url);
self.build_box_info_for_image(node, data)
}
- ElementNodeTypeId(HTMLTableElementTypeId) => TableWrapperBox,
- ElementNodeTypeId(HTMLTableColElementTypeId) => TableColumnBox(TableColumnBoxInfo::new(node)),
- ElementNodeTypeId(HTMLTableDataCellElementTypeId) |
- ElementNodeTypeId(HTMLTableHeaderCellElementTypeId) => TableCellBox,
- ElementNodeTypeId(HTMLTableRowElementTypeId) |
- ElementNodeTypeId(HTMLTableSectionElementTypeId) => TableRowBox,
- TextNodeTypeId => UnscannedTextBox(UnscannedTextBoxInfo::new(node)),
+ Some(ElementNodeTypeId(HTMLTableElementTypeId)) => TableWrapperBox,
+ Some(ElementNodeTypeId(HTMLTableColElementTypeId)) => {
+ TableColumnBox(TableColumnBoxInfo::new(node))
+ }
+ Some(ElementNodeTypeId(HTMLTableDataCellElementTypeId)) |
+ Some(ElementNodeTypeId(HTMLTableHeaderCellElementTypeId)) => TableCellBox,
+ Some(ElementNodeTypeId(HTMLTableRowElementTypeId)) |
+ Some(ElementNodeTypeId(HTMLTableSectionElementTypeId)) => TableRowBox,
+ None | Some(TextNodeTypeId) => UnscannedTextBox(UnscannedTextBoxInfo::new(node)),
_ => GenericBox,
}
}
@@ -352,6 +359,115 @@ impl<'a> FlowConstructor<'a> {
}
}
+ fn build_block_flow_using_children_construction_result(&mut self,
+ flow: &mut ~Flow,
+ consecutive_siblings: &mut ~[~Flow],
+ node: &ThreadSafeLayoutNode,
+ kid: ThreadSafeLayoutNode,
+ opt_boxes_for_inline_flow: &mut Option<~[Box]>,
+ abs_descendants: &mut Descendants,
+ first_box: &mut bool) {
+ match kid.swap_out_construction_result() {
+ NoConstructionResult => {}
+ FlowConstructionResult(kid_flow, kid_abs_descendants) => {
+ // If kid_flow is TableCaptionFlow, kid_flow should be added under
+ // TableWrapperFlow.
+ if flow.is_table() && kid_flow.is_table_caption() {
+ kid.set_flow_construction_result(FlowConstructionResult(
+ kid_flow,
+ Descendants::new()))
+ } else if flow.need_anonymous_flow(kid_flow) {
+ consecutive_siblings.push(kid_flow)
+ } else {
+ // Strip ignorable whitespace from the start of this flow per CSS 2.1 §
+ // 9.2.1.1.
+ if flow.is_table_kind() || *first_box {
+ strip_ignorable_whitespace_from_start(opt_boxes_for_inline_flow);
+ *first_box = false
+ }
+
+ // Flush any inline boxes that we were gathering up. This allows us to handle
+ // {ib} splits.
+ debug!("flushing {} inline box(es) to flow A",
+ opt_boxes_for_inline_flow.as_ref()
+ .map_or(0, |boxes| boxes.len()));
+ self.flush_inline_boxes_to_flow_or_list_if_necessary(
+ opt_boxes_for_inline_flow,
+ flow,
+ consecutive_siblings,
+ node);
+ if !consecutive_siblings.is_empty() {
+ let consecutive_siblings = mem::replace(consecutive_siblings, ~[]);
+ self.generate_anonymous_missing_child(consecutive_siblings,
+ flow,
+ node);
+ }
+ flow.add_new_child(kid_flow);
+ }
+ abs_descendants.push_descendants(kid_abs_descendants);
+ }
+ ConstructionItemConstructionResult(InlineBoxesConstructionItem(
+ InlineBoxesConstructionResult {
+ splits: opt_splits,
+ boxes: boxes,
+ abs_descendants: kid_abs_descendants,
+ })) => {
+ // Add any {ib} splits.
+ match opt_splits {
+ None => {}
+ Some(splits) => {
+ for split in splits.move_iter() {
+ // Pull apart the {ib} split object and push its predecessor boxes
+ // onto the list.
+ let InlineBlockSplit {
+ predecessor_boxes: predecessor_boxes,
+ flow: kid_flow
+ } = split;
+ opt_boxes_for_inline_flow.push_all_move(predecessor_boxes);
+
+ // If this is the first box in flow, then strip ignorable
+ // whitespace per CSS 2.1 § 9.2.1.1.
+ if *first_box {
+ strip_ignorable_whitespace_from_start(
+ opt_boxes_for_inline_flow);
+ *first_box = false
+ }
+
+ // Flush any inline boxes that we were gathering up.
+ debug!("flushing {} inline box(es) to flow A",
+ opt_boxes_for_inline_flow.as_ref()
+ .map_or(0, |boxes| boxes.len()));
+ self.flush_inline_boxes_to_flow_or_list_if_necessary(
+ opt_boxes_for_inline_flow,
+ flow,
+ consecutive_siblings,
+ node);
+
+ // Push the flow generated by the {ib} split onto our list of
+ // flows.
+ if flow.need_anonymous_flow(kid_flow) {
+ consecutive_siblings.push(kid_flow)
+ } else {
+ flow.add_new_child(kid_flow)
+ }
+ }
+ }
+ }
+
+ // Add the boxes to the list we're maintaining.
+ opt_boxes_for_inline_flow.push_all_move(boxes);
+ abs_descendants.push_descendants(kid_abs_descendants);
+ }
+ ConstructionItemConstructionResult(WhitespaceConstructionItem(..)) => {
+ // Nothing to do here.
+ }
+ ConstructionItemConstructionResult(TableColumnBoxConstructionItem(_)) => {
+ // TODO: Implement anonymous table objects for missing parents
+ // CSS 2.1 § 17.2.1, step 3-2
+ }
+ }
+ }
+
/// Build block flow for current node using information from children nodes.
///
/// Consume results from children and combine them, handling {ib} splits.
@@ -367,109 +483,22 @@ impl<'a> FlowConstructor<'a> {
let mut opt_boxes_for_inline_flow = None;
let mut consecutive_siblings = ~[];
let mut first_box = true;
+
// List of absolute descendants, in tree order.
let mut abs_descendants = Descendants::new();
- let mut fixed_descendants = Descendants::new();
for kid in node.children() {
- match kid.swap_out_construction_result() {
- NoConstructionResult => {}
- FlowConstructionResult(kid_flow, kid_abs_descendants, kid_fixed_descendants) => {
- // If kid_flow is TableCaptionFlow, kid_flow should be added under TableWrapperFlow.
- if flow.is_table() && kid_flow.is_table_caption() {
- kid.set_flow_construction_result(FlowConstructionResult(kid_flow,
- Descendants::new(),
- Descendants::new()))
- } else if flow.need_anonymous_flow(kid_flow) {
- consecutive_siblings.push(kid_flow)
- } else {
- // Strip ignorable whitespace from the start of this flow per CSS 2.1 §
- // 9.2.1.1.
- if flow.is_table_kind() || first_box {
- strip_ignorable_whitespace_from_start(&mut opt_boxes_for_inline_flow);
- first_box = false
- }
-
- // Flush any inline boxes that we were gathering up. This allows us to handle
- // {ib} splits.
- debug!("flushing {} inline box(es) to flow A",
- opt_boxes_for_inline_flow.as_ref()
- .map_or(0, |boxes| boxes.len()));
- self.flush_inline_boxes_to_flow_or_list_if_necessary(&mut opt_boxes_for_inline_flow,
- &mut flow,
- &mut consecutive_siblings,
- node);
- if !consecutive_siblings.is_empty() {
- self.generate_anonymous_missing_child(consecutive_siblings, &mut flow, node);
- consecutive_siblings = ~[];
- }
- flow.add_new_child(kid_flow);
- }
- abs_descendants.push_descendants(kid_abs_descendants);
- fixed_descendants.push_descendants(kid_fixed_descendants);
- }
- ConstructionItemConstructionResult(InlineBoxesConstructionItem(
- InlineBoxesConstructionResult {
- splits: opt_splits,
- boxes: boxes,
- abs_descendants: kid_abs_descendants,
- fixed_descendants: kid_fixed_descendants,
- })) => {
- // Add any {ib} splits.
- match opt_splits {
- None => {}
- Some(splits) => {
- for split in splits.move_iter() {
- // Pull apart the {ib} split object and push its predecessor boxes
- // onto the list.
- let InlineBlockSplit {
- predecessor_boxes: predecessor_boxes,
- flow: kid_flow
- } = split;
- opt_boxes_for_inline_flow.push_all_move(predecessor_boxes);
-
- // If this is the first box in flow, then strip ignorable
- // whitespace per CSS 2.1 § 9.2.1.1.
- if first_box {
- strip_ignorable_whitespace_from_start(
- &mut opt_boxes_for_inline_flow);
- first_box = false
- }
-
- // Flush any inline boxes that we were gathering up.
- debug!("flushing {} inline box(es) to flow A",
- opt_boxes_for_inline_flow.as_ref()
- .map_or(0,
- |boxes| boxes.len()));
- self.flush_inline_boxes_to_flow_or_list_if_necessary(
- &mut opt_boxes_for_inline_flow,
- &mut flow,
- &mut consecutive_siblings,
- node);
-
- // Push the flow generated by the {ib} split onto our list of
- // flows.
- if flow.need_anonymous_flow(kid_flow) {
- consecutive_siblings.push(kid_flow)
- } else {
- flow.add_new_child(kid_flow)
- }
- }
- }
- }
-
- // Add the boxes to the list we're maintaining.
- opt_boxes_for_inline_flow.push_all_move(boxes);
- abs_descendants.push_descendants(kid_abs_descendants);
- fixed_descendants.push_descendants(kid_fixed_descendants);
- }
- ConstructionItemConstructionResult(WhitespaceConstructionItem(..)) => {
- // Nothing to do here.
- }
- ConstructionItemConstructionResult(TableColumnBoxConstructionItem(_)) => {
- // TODO: Implement anonymous table objects for missing parents
- // CSS 2.1 § 17.2.1, step 3-2
- }
+ if kid.get_element_type() != Normal {
+ self.process(&kid);
}
+
+ self.build_block_flow_using_children_construction_result(
+ &mut flow,
+ &mut consecutive_siblings,
+ node,
+ kid,
+ &mut opt_boxes_for_inline_flow,
+ &mut abs_descendants,
+ &mut first_box);
}
// Perform a final flush of any inline boxes that we were gathering up to handle {ib}
@@ -493,16 +522,13 @@ impl<'a> FlowConstructor<'a> {
flow.set_abs_descendants(abs_descendants);
abs_descendants = Descendants::new();
- if is_fixed_positioned {
- // Send itself along with the other fixed descendants.
- fixed_descendants.push(Rawlink::some(flow));
- } else if is_absolutely_positioned {
+ if is_fixed_positioned || is_absolutely_positioned {
// This is now the only absolute flow in the subtree which hasn't yet
// reached its CB.
abs_descendants.push(Rawlink::some(flow));
}
}
- FlowConstructionResult(flow, abs_descendants, fixed_descendants)
+ FlowConstructionResult(flow, abs_descendants)
}
/// Builds a flow for a node with `display: block`. This yields a `BlockFlow` with possibly
@@ -521,7 +547,6 @@ impl<'a> FlowConstructor<'a> {
self.build_flow_using_children(flow, node)
}
-
/// Concatenates the boxes of kids, adding in our own borders/padding/margins if necessary.
/// Returns the `InlineBoxesConstructionResult`, if any. There will be no
/// `InlineBoxesConstructionResult` if this node consisted entirely of ignorable whitespace.
@@ -530,13 +555,12 @@ impl<'a> FlowConstructor<'a> {
let mut opt_inline_block_splits = None;
let mut opt_box_accumulator = None;
let mut abs_descendants = Descendants::new();
- let mut fixed_descendants = Descendants::new();
// Concatenate all the boxes of our kids, creating {ib} splits as necessary.
for kid in node.children() {
match kid.swap_out_construction_result() {
NoConstructionResult => {}
- FlowConstructionResult(flow, kid_abs_descendants, kid_fixed_descendants) => {
+ FlowConstructionResult(flow, kid_abs_descendants) => {
// {ib} split. Flush the accumulator to our new split and make a new
// accumulator to hold any subsequent boxes we come across.
let split = InlineBlockSplit {
@@ -545,14 +569,12 @@ impl<'a> FlowConstructor<'a> {
};
opt_inline_block_splits.push(split);
abs_descendants.push_descendants(kid_abs_descendants);
- fixed_descendants.push_descendants(kid_fixed_descendants);
}
ConstructionItemConstructionResult(InlineBoxesConstructionItem(
InlineBoxesConstructionResult {
splits: opt_splits,
boxes: boxes,
abs_descendants: kid_abs_descendants,
- fixed_descendants: kid_fixed_descendants,
})) => {
// Bubble up {ib} splits.
@@ -579,7 +601,6 @@ impl<'a> FlowConstructor<'a> {
// Push residual boxes.
opt_box_accumulator.push_all_move(boxes);
abs_descendants.push_descendants(kid_abs_descendants);
- fixed_descendants.push_descendants(kid_fixed_descendants);
}
ConstructionItemConstructionResult(WhitespaceConstructionItem(whitespace_node,
whitespace_style))
@@ -612,7 +633,7 @@ impl<'a> FlowConstructor<'a> {
for box_ in boxes.iter() {
total.push(box_);
}
- self.set_inline_info_for_inline_child(&total, node);
+ self.set_inline_info_for_inline_child(total, node);
},
None => {
@@ -622,7 +643,7 @@ impl<'a> FlowConstructor<'a> {
total.push(box_);
}
}
- self.set_inline_info_for_inline_child(&total, node);
+ self.set_inline_info_for_inline_child(total, node);
}
}
},
@@ -633,7 +654,7 @@ impl<'a> FlowConstructor<'a> {
for box_ in boxes.iter() {
total.push(box_);
}
- self.set_inline_info_for_inline_child(&total, node);
+ self.set_inline_info_for_inline_child(total, node);
},
None => {}
}
@@ -648,7 +669,6 @@ impl<'a> FlowConstructor<'a> {
splits: opt_inline_block_splits,
boxes: opt_box_accumulator.to_vec(),
abs_descendants: abs_descendants,
- fixed_descendants: fixed_descendants,
});
ConstructionItemConstructionResult(construction_item)
} else {
@@ -656,8 +676,9 @@ impl<'a> FlowConstructor<'a> {
}
}
+ // FIXME(#1999, pcwalton): Why does this function create a box only to throw it away???
fn set_inline_info_for_inline_child(&mut self,
- boxes: &~[&Box],
+ boxes: &[&Box],
parent_node: &ThreadSafeLayoutNode) {
let parent_box = Box::new(self, parent_node);
let font_style = parent_box.font_style();
@@ -671,33 +692,39 @@ impl<'a> FlowConstructor<'a> {
let boxes_len = boxes.len();
parent_box.compute_borders(parent_box.style());
+ // FIXME(#2000, pcwalton): I suspect that `Au(0)` is not correct for the containing block
+ // width.
+ parent_box.compute_padding(parent_box.style(), Au(0));
+
for (i, box_) in boxes.iter().enumerate() {
if box_.inline_info.with( |data| data.is_none() ) {
box_.inline_info.set(Some(InlineInfo::new()));
}
let mut border = parent_box.border.get();
+ let mut padding = parent_box.padding.get();
if i != 0 {
border.left = Zero::zero();
+ padding.left = Zero::zero()
}
if i != (boxes_len - 1) {
border.right = Zero::zero();
+ padding.right = Zero::zero()
}
let mut info = box_.inline_info.borrow_mut();
match info.get() {
&Some(ref mut info) => {
- // TODO(ksh8281) compute margin,padding
- info.parent_info.push(
- InlineParentInfo {
- padding: Zero::zero(),
- border: border,
- margin: Zero::zero(),
- style: parent_box.style.clone(),
- font_ascent: font_ascent,
- font_descent: font_descent,
- node: OpaqueNode::from_thread_safe_layout_node(parent_node),
- });
+ // TODO(ksh8281): Compute margins.
+ info.parent_info.push(InlineParentInfo {
+ padding: padding,
+ border: border,
+ margin: Zero::zero(),
+ style: parent_box.style.clone(),
+ font_ascent: font_ascent,
+ font_descent: font_descent,
+ node: OpaqueNodeMethods::from_thread_safe_layout_node(parent_node),
+ })
},
&None => {}
}
@@ -712,20 +739,22 @@ impl<'a> FlowConstructor<'a> {
}
// If this node is ignorable whitespace, bail out now.
+ //
+ // FIXME(#2001, pcwalton): Don't do this if there's padding or borders.
if node.is_ignorable_whitespace() {
- let opaque_node = OpaqueNode::from_thread_safe_layout_node(node);
+ let opaque_node = OpaqueNodeMethods::from_thread_safe_layout_node(node);
return ConstructionItemConstructionResult(WhitespaceConstructionItem(
opaque_node,
node.style().clone()))
}
+ let mut opt_box_accumulator = None;
+ opt_box_accumulator.push(Box::new(self, node));
+
let construction_item = InlineBoxesConstructionItem(InlineBoxesConstructionResult {
splits: None,
- boxes: ~[
- Box::new(self, node)
- ],
+ boxes: opt_box_accumulator.to_vec(),
abs_descendants: Descendants::new(),
- fixed_descendants: Descendants::new(),
});
ConstructionItemConstructionResult(construction_item)
}
@@ -750,7 +779,7 @@ impl<'a> FlowConstructor<'a> {
for kid in node.children() {
match kid.swap_out_construction_result() {
NoConstructionResult | ConstructionItemConstructionResult(_) => {}
- FlowConstructionResult(kid_flow, _, _) => {
+ FlowConstructionResult(kid_flow, _) => {
// Only kid flows with table-caption are matched here.
assert!(kid_flow.is_table_caption());
table_wrapper_flow.add_new_child(kid_flow);
@@ -761,8 +790,10 @@ impl<'a> FlowConstructor<'a> {
/// Generates an anonymous table flow according to CSS 2.1 § 17.2.1, step 2.
/// If necessary, generate recursively another anonymous table flow.
- fn generate_anonymous_missing_child(&mut self, child_flows: ~[~Flow],
- flow: &mut ~Flow, node: &ThreadSafeLayoutNode) {
+ fn generate_anonymous_missing_child(&mut self,
+ child_flows: ~[~Flow],
+ flow: &mut ~Flow,
+ node: &ThreadSafeLayoutNode) {
let mut anonymous_flow = flow.generate_missing_child_flow(node);
let mut consecutive_siblings = ~[];
for kid_flow in child_flows.move_iter() {
@@ -805,10 +836,9 @@ impl<'a> FlowConstructor<'a> {
// NOTE: The order of captions and table are not the same order as in the DOM tree.
// All caption blocks are placed before the table flow
match construction_result {
- FlowConstructionResult(table_flow, table_abs_descendants, table_fixed_descendants) => {
+ FlowConstructionResult(table_flow, table_abs_descendants) => {
wrapper_flow.add_new_child(table_flow);
abs_descendants.push_descendants(table_abs_descendants);
- fixed_descendants.push_descendants(table_fixed_descendants);
}
_ => {}
}
@@ -832,7 +862,7 @@ impl<'a> FlowConstructor<'a> {
abs_descendants.push(Rawlink::some(wrapper_flow));
}
}
- FlowConstructionResult(wrapper_flow, abs_descendants, fixed_descendants)
+ FlowConstructionResult(wrapper_flow, abs_descendants)
}
/// Builds a flow for a node with `display: table-caption`. This yields a `TableCaptionFlow`
@@ -904,7 +934,7 @@ impl<'a> FlowConstructor<'a> {
let mut flow = ~TableColGroupFlow::from_node_and_boxes(node, box_, col_boxes) as ~Flow;
flow.finish(self.layout_context);
- FlowConstructionResult(flow, Descendants::new(), Descendants::new())
+ FlowConstructionResult(flow, Descendants::new())
}
}
@@ -922,16 +952,23 @@ impl<'a> PostorderNodeMutTraversal for FlowConstructor<'a> {
fn process(&mut self, node: &ThreadSafeLayoutNode) -> bool {
// Get the `display` property for this node, and determine whether this node is floated.
let (display, float, positioning) = match node.type_id() {
- ElementNodeTypeId(_) => {
+ None => {
+ // Pseudo-element.
+ let style = node.style().get();
+ (display::inline, style.Box.get().float, style.Box.get().position)
+ }
+ Some(ElementNodeTypeId(_)) => {
let style = node.style().get();
(style.Box.get().display, style.Box.get().float, style.Box.get().position)
}
- TextNodeTypeId => (display::inline, float::none, position::static_),
- CommentNodeTypeId |
- DoctypeNodeTypeId |
- DocumentFragmentNodeTypeId |
- DocumentNodeTypeId |
- ProcessingInstructionNodeTypeId => (display::none, float::none, position::static_),
+ Some(TextNodeTypeId) => (display::inline, float::none, position::static_),
+ Some(CommentNodeTypeId) |
+ Some(DoctypeNodeTypeId) |
+ Some(DocumentFragmentNodeTypeId) |
+ Some(DocumentNodeTypeId) |
+ Some(ProcessingInstructionNodeTypeId) => {
+ (display::none, float::none, position::static_)
+ }
};
debug!("building flow for node: {:?} {:?}", display, float);
@@ -1044,21 +1081,22 @@ trait NodeUtils {
impl<'ln> NodeUtils for ThreadSafeLayoutNode<'ln> {
fn is_replaced_content(&self) -> bool {
match self.type_id() {
- TextNodeTypeId |
- ProcessingInstructionNodeTypeId |
- CommentNodeTypeId |
- DoctypeNodeTypeId |
- DocumentFragmentNodeTypeId |
- DocumentNodeTypeId |
- ElementNodeTypeId(HTMLImageElementTypeId) => true,
- ElementNodeTypeId(HTMLObjectElementTypeId) => self.has_object_data(),
- ElementNodeTypeId(_) => false,
+ Some(TextNodeTypeId) |
+ Some(ProcessingInstructionNodeTypeId) |
+ Some(CommentNodeTypeId) |
+ Some(DoctypeNodeTypeId) |
+ Some(DocumentFragmentNodeTypeId) |
+ Some(DocumentNodeTypeId) |
+ None |
+ Some(ElementNodeTypeId(HTMLImageElementTypeId)) => true,
+ Some(ElementNodeTypeId(HTMLObjectElementTypeId)) => self.has_object_data(),
+ Some(ElementNodeTypeId(_)) => false,
}
}
fn is_ignorable_whitespace(&self) -> bool {
match self.type_id() {
- TextNodeTypeId => {
+ Some(TextNodeTypeId) => {
unsafe {
let text: JS<Text> = TextCast::to(self.get_jsmanaged()).unwrap();
if !is_whitespace(text.get().characterdata.data) {
@@ -1085,7 +1123,17 @@ impl<'ln> NodeUtils for ThreadSafeLayoutNode<'ln> {
fn set_flow_construction_result(&self, result: ConstructionResult) {
let mut layout_data_ref = self.mutate_layout_data();
match *layout_data_ref.get() {
- Some(ref mut layout_data) => layout_data.data.flow_construction_result = result,
+ Some(ref mut layout_data) =>{
+ match self.get_element_type() {
+ Before | BeforeBlock => {
+ layout_data.data.before_flow_construction_result = result
+ },
+ After | AfterBlock => {
+ layout_data.data.after_flow_construction_result = result
+ },
+ Normal => layout_data.data.flow_construction_result = result,
+ }
+ },
None => fail!("no layout data"),
}
}
@@ -1095,7 +1143,20 @@ impl<'ln> NodeUtils for ThreadSafeLayoutNode<'ln> {
let mut layout_data_ref = self.mutate_layout_data();
match *layout_data_ref.get() {
Some(ref mut layout_data) => {
- mem::replace(&mut layout_data.data.flow_construction_result, NoConstructionResult)
+ match self.get_element_type() {
+ Before | BeforeBlock => {
+ mem::replace(&mut layout_data.data.before_flow_construction_result,
+ NoConstructionResult)
+ }
+ After | AfterBlock => {
+ mem::replace(&mut layout_data.data.after_flow_construction_result,
+ NoConstructionResult)
+ }
+ Normal => {
+ mem::replace(&mut layout_data.data.flow_construction_result,
+ NoConstructionResult)
+ }
+ }
}
None => fail!("no layout data"),
}
diff --git a/src/components/main/layout/context.rs b/src/components/main/layout/context.rs
index 4e3ea7bb404..558d42924be 100644
--- a/src/components/main/layout/context.rs
+++ b/src/components/main/layout/context.rs
@@ -5,10 +5,10 @@
//! Data needed by the layout task.
use css::matching::{ApplicableDeclarationsCache, StyleSharingCandidateCache};
-use layout::util::OpaqueNode;
use extra::url::Url;
use geom::size::Size2D;
+use gfx::display_list::OpaqueNode;
use gfx::font_context::{FontContext, FontContextInfo};
use green::task::GreenTask;
use script::layout_interface::LayoutChan;
diff --git a/src/components/main/layout/display_list_builder.rs b/src/components/main/layout/display_list_builder.rs
index dc20f784667..3bec56473c3 100644
--- a/src/components/main/layout/display_list_builder.rs
+++ b/src/components/main/layout/display_list_builder.rs
@@ -4,39 +4,34 @@
//! Constructs display lists from boxes.
-use layout::box_::Box;
use layout::context::LayoutContext;
-use layout::util::OpaqueNode;
+use geom::{Point2D, Rect, Size2D};
+use gfx::render_task::RenderLayer;
use gfx;
+use servo_util::geometry::Au;
+use servo_util::smallvec::SmallVec0;
use style;
-pub trait ExtraDisplayListData {
- fn new(box_: &Box) -> Self;
-}
+/// Manages the information needed to construct the display list.
+pub struct DisplayListBuilder<'a> {
+ ctx: &'a LayoutContext,
-pub type Nothing = ();
+ /// A list of render layers that we've built up, root layer not included.
+ layers: SmallVec0<RenderLayer>,
-impl ExtraDisplayListData for OpaqueNode {
- fn new(box_: &Box) -> OpaqueNode {
- box_.node
- }
+ /// The dirty rect.
+ dirty: Rect<Au>,
}
-impl ExtraDisplayListData for Nothing {
- fn new(_: &Box) -> Nothing {
- ()
- }
-}
-
-/// 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.
-///
-/// Right now, the builder isn't used for much, but it establishes the pattern we'll need once we
-/// support display-list-based hit testing and so forth.
-pub struct DisplayListBuilder<'a> {
- ctx: &'a LayoutContext,
+/// Information needed at each step of the display list building traversal.
+pub struct DisplayListBuildingInfo {
+ /// The size of the containing block for relatively-positioned descendants.
+ relative_containing_block_size: Size2D<Au>,
+ /// The position and size of the absolute containing block.
+ absolute_containing_block_position: Point2D<Au>,
+ /// Whether the absolute containing block forces positioned descendants to be layerized.
+ layers_needed_for_positioned_flows: bool,
}
//
diff --git a/src/components/main/layout/flow.rs b/src/components/main/layout/flow.rs
index 099f79c8e81..d9bcb967670 100644
--- a/src/components/main/layout/flow.rs
+++ b/src/components/main/layout/flow.rs
@@ -26,14 +26,16 @@
/// similar methods.
use css::node_style::StyledNode;
-use layout::block::{BlockFlow};
+use layout::block::BlockFlow;
use layout::box_::{Box, TableRowBox, TableCellBox};
-use layout::context::LayoutContext;
use layout::construct::OptVector;
-use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData};
+use layout::context::LayoutContext;
+use layout::display_list_builder::{DisplayListBuilder, DisplayListBuildingInfo, ToGfxColor};
use layout::floats::Floats;
+use layout::flow_list::{FlowList, Link, Rawlink, FlowListIterator, MutFlowListIterator};
use layout::incremental::RestyleDamage;
use layout::inline::InlineFlow;
+use layout::model::{CollapsibleMargins, IntrinsicWidths, MarginCollapseInfo};
use layout::parallel::FlowParallelInfo;
use layout::parallel;
use layout::table_wrapper::TableWrapperFlow;
@@ -44,24 +46,22 @@ use layout::table_row::TableRowFlow;
use layout::table_caption::TableCaptionFlow;
use layout::table_cell::TableCellFlow;
use layout::wrapper::ThreadSafeLayoutNode;
-use layout::flow_list::{FlowList, Link, Rawlink, FlowListIterator, MutFlowListIterator};
use collections::Deque;
-use geom::point::Point2D;
use geom::Size2D;
+use geom::point::Point2D;
use geom::rect::Rect;
-use gfx::display_list::{ClipDisplayItemClass, DisplayListCollection, DisplayList};
-use layout::display_list_builder::ToGfxColor;
use gfx::color::Color;
-use servo_util::smallvec::{SmallVec, SmallVec0};
+use gfx::display_list::StackingContext;
+use servo_msg::compositor_msg::LayerId;
use servo_util::geometry::Au;
+use servo_util::smallvec::{SmallVec, SmallVec0};
use std::cast;
-use std::cell::RefCell;
+use std::iter::Zip;
use std::sync::atomics::Relaxed;
use std::vec::MutItems;
-use std::iter::Zip;
use style::ComputedValues;
-use style::computed_values::{text_align, position};
+use style::computed_values::{clear, position, text_align};
/// Virtual methods that make up a float context.
///
@@ -126,6 +126,24 @@ pub trait Flow {
fail!("called as_table_cell() on a non-tablecell flow")
}
+ /// If this is a table row or table rowgroup or table flow, returns column widths.
+ /// Fails otherwise.
+ fn col_widths<'a>(&'a mut self) -> &'a mut ~[Au] {
+ fail!("called col_widths() on an other flow than table-row/table-rowgroup/table")
+ }
+
+ /// If this is a table row flow or table rowgroup flow or table flow, returns column min widths.
+ /// Fails otherwise.
+ fn col_min_widths<'a>(&'a self) -> &'a ~[Au] {
+ fail!("called col_min_widths() on an other flow than table-row/table-rowgroup/table")
+ }
+
+ /// If this is a table row flow or table rowgroup flow or table flow, returns column min widths.
+ /// Fails otherwise.
+ fn col_pref_widths<'a>(&'a self) -> &'a ~[Au] {
+ fail!("called col_pref_widths() on an other flow than table-row/table-rowgroup/table")
+ }
+
// Main methods
/// Pass 1 of reflow: computes minimum and preferred widths.
@@ -148,15 +166,10 @@ pub trait Flow {
fail!("assign_height_inorder not yet implemented")
}
- /// Collapses margins with the parent flow. This runs as part of assign-heights.
- fn collapse_margins(&mut self,
- _top_margin_collapsible: bool,
- _first_in_flow: &mut bool,
- _margin_top: &mut Au,
- _top_offset: &mut Au,
- _collapsing: &mut Au,
- _collapsible: &mut Au) {
- fail!("collapse_margins not yet implemented")
+ fn compute_collapsible_top_margin(&mut self,
+ _layout_context: &mut LayoutContext,
+ _margin_collapse_info: &mut MarginCollapseInfo) {
+ // The default implementation is a no-op.
}
/// Marks this flow as the root flow. The default implementation is a no-op.
@@ -209,6 +222,11 @@ pub trait Flow {
false
}
+ /// Returns true if this is an absolute containing block.
+ fn is_absolute_containing_block(&self) -> bool {
+ false
+ }
+
/// Return the dimensions of the CB generated _by_ this flow for absolute descendants.
fn generated_cb_size(&self) -> Size2D<Au> {
fail!("generated_cb_size not yet implemented")
@@ -219,6 +237,14 @@ pub trait Flow {
fail!("this is not the CB-generating flow you're looking for")
}
+ /// Returns a layer ID for the given fragment.
+ fn layer_id(&self, fragment_id: uint) -> LayerId {
+ unsafe {
+ let pointer: uint = cast::transmute(self);
+ LayerId(pointer, fragment_id)
+ }
+ }
+
/// Returns a debugging string describing this flow.
fn debug_str(&self) -> ~str {
~"???"
@@ -336,16 +362,11 @@ pub trait MutableFlowUtils {
/// Computes the overflow region for this flow.
fn store_overflow(self, _: &mut LayoutContext);
- /// builds the display lists
- fn build_display_lists<E:ExtraDisplayListData>(
- self,
- builder: &DisplayListBuilder,
- container_block_size: &Size2D<Au>,
- absolute_cb_abs_position: Point2D<Au>,
- dirty: &Rect<Au>,
- index: uint,
- mut list: &RefCell<DisplayListCollection<E>>)
- -> bool;
+ /// Builds the display lists for this flow and its descendants.
+ fn build_display_list(self,
+ stacking_context: &mut StackingContext,
+ builder: &mut DisplayListBuilder,
+ info: &DisplayListBuildingInfo);
/// Destroys the flow.
fn destroy(self);
@@ -369,11 +390,6 @@ pub trait MutableOwnedFlowUtils {
/// Set this flow as the Containing Block for all the absolute descendants.
fn set_abs_descendants(&mut self, abs_descendants: AbsDescendants);
- /// Set fixed descendants for this flow.
- ///
- /// Set yourself as the Containing Block for all the fixed descendants.
- fn set_fixed_descendants(&mut self, fixed_descendants: AbsDescendants);
-
/// Destroys the flow.
fn destroy(&mut self);
}
@@ -606,6 +622,17 @@ bitfield!(FlowFlags, override_overline, set_override_overline, 0b0000_0100)
// NB: If you update this, you need to update TEXT_DECORATION_OVERRIDE_BITMASK.
bitfield!(FlowFlags, override_line_through, set_override_line_through, 0b0000_1000)
+// Whether this flow contains a flow that has its own layer within the same absolute containing
+// block.
+bitfield!(FlowFlags,
+ layers_needed_for_descendants,
+ set_layers_needed_for_descendants,
+ 0b0100_0000)
+
+// Whether this flow must have its own layer. Even if this flag is not set, it might get its own
+// layer if it's deemed to be likely to overlap flows with their own layer.
+bitfield!(FlowFlags, needs_layer, set_needs_layer, 0b1000_0000)
+
// The text alignment for this flow.
impl FlowFlags {
#[inline]
@@ -690,7 +717,6 @@ impl Descendants {
}
pub type AbsDescendants = Descendants;
-pub type FixedDescendants = Descendants;
type DescendantIter<'a> = MutItems<'a, Rawlink>;
@@ -708,12 +734,16 @@ pub struct BaseFlow {
/* layout computations */
// TODO: min/pref and position are used during disjoint phases of
// layout; maybe combine into a single enum to save space.
- min_width: Au,
- pref_width: Au,
+ intrinsic_widths: IntrinsicWidths,
- /// The upper left corner of the box representing this flow, relative to
- /// the box representing its parent flow.
- /// For absolute flows, this represents the position wrt to its Containing Block.
+ /// The upper left corner of the box representing this flow, relative to the box representing
+ /// its parent flow.
+ ///
+ /// For absolute flows, this represents the position with respect to its *containing block*.
+ ///
+ /// This does not include margins in the block flow direction, because those can collapse. So
+ /// for the block direction (usually vertical), this represents the *border box*. For the
+ /// inline direction (usually horizontal), this represents the *margin box*.
position: Rect<Au>,
/// The amount of overflow of this flow, relative to the containing block. Must include all the
@@ -728,6 +758,9 @@ pub struct BaseFlow {
/// The floats next to this flow.
floats: Floats,
+ /// The value of this flow's `clear` property, if any.
+ clear: clear::T,
+
/// For normal flows, this is the number of floated descendants that are
/// not contained within any other floated descendant of this flow. For
/// floats, it is 1.
@@ -735,15 +768,15 @@ pub struct BaseFlow {
/// decide whether to do an in-order traversal for assign_height.
num_floats: uint,
+ /// The collapsible margins for this flow, if any.
+ collapsible_margins: CollapsibleMargins,
+
/// The position of this flow in page coordinates, computed during display list construction.
abs_position: Point2D<Au>,
- /// Details about descendants with position 'absolute' for which we are
- /// the CB. This is in tree order. This includes any direct children.
+ /// Details about descendants with position 'absolute' or 'fixed' for which we are the
+ /// containing block. This is in tree order. This includes any direct children.
abs_descendants: AbsDescendants,
- /// Details about descendants with position 'fixed'.
- /// TODO: Optimize this, because this will be set only for the root.
- fixed_descendants: FixedDescendants,
/// Offset wrt the nearest positioned ancestor - aka the Containing Block
/// for any absolutely positioned elements.
@@ -800,8 +833,7 @@ impl BaseFlow {
next_sibling: None,
prev_sibling: Rawlink::none(),
- min_width: Au::new(0),
- pref_width: Au::new(0),
+ intrinsic_widths: IntrinsicWidths::new(),
position: Au::zero_rect(),
overflow: Au::zero_rect(),
@@ -809,9 +841,10 @@ impl BaseFlow {
floats: Floats::new(),
num_floats: 0,
+ collapsible_margins: CollapsibleMargins::new(),
+ clear: clear::none,
abs_position: Point2D(Au::new(0), Au::new(0)),
abs_descendants: Descendants::new(),
- fixed_descendants: Descendants::new(),
absolute_static_x_offset: Au::new(0),
fixed_static_x_offset: Au::new(0),
absolute_cb: Rawlink::none(),
@@ -1068,6 +1101,7 @@ impl<'a> MutableFlowUtils for &'a mut Flow {
overflow = overflow.union(&kid_overflow)
}
+ // FIXME(#2004, pcwalton): This is wrong for `position: fixed`.
for descendant_link in mut_base(self).abs_descendants.iter() {
match descendant_link.resolve() {
Some(flow) => {
@@ -1078,183 +1112,62 @@ impl<'a> MutableFlowUtils for &'a mut Flow {
None => fail!("empty Rawlink to a descendant")
}
}
-
- if self.is_root() {
- for fixed_descendant_link in mut_base(self).fixed_descendants.iter() {
- match fixed_descendant_link.resolve() {
- Some(flow) => {
- let mut kid_overflow = base(flow).overflow;
- kid_overflow = kid_overflow.translate(&my_position.origin);
- overflow = overflow.union(&kid_overflow)
- }
- None => fail!("empty Rawlink to a descendant")
- }
- }
- }
}
mut_base(self).overflow = overflow;
}
- /// Push display items for current flow and its children onto `list`.
+ /// Push display items for current flow and its descendants onto the appropriate display lists
+ /// of the given stacking context.
+ ///
+ /// Arguments:
///
- /// For InlineFlow, add display items for all its boxes onto list`.
- /// For BlockFlow, add a ClipDisplayItemClass for itself and its children,
- /// plus any other display items like border.
+ /// * `stacking_context`: The parent stacking context that this flow belongs to and to which
+ /// display items will be added.
///
- /// `container_block_size`: Size of the Containing Block for the current
- /// flow. This is used for relative positioning (which resolves percentage
- /// values for 'top', etc. after all Containing Block heights have been computed.)
- /// `absolute_cb_abs_position`: Absolute position of the Containing Block
- /// for the flow if it is absolutely positioned.
- fn build_display_lists<E:ExtraDisplayListData>(
- self,
- builder: &DisplayListBuilder,
- container_block_size: &Size2D<Au>,
- absolute_cb_abs_position: Point2D<Au>,
- dirty: &Rect<Au>,
- mut index: uint,
- lists: &RefCell<DisplayListCollection<E>>)
- -> bool {
+ /// * `builder`: The display list builder, which contains information used during the entire
+ /// display list building pass.
+ ///
+ /// * `info`: Per-flow display list building information.
+ fn build_display_list(self,
+ stacking_context: &mut StackingContext,
+ builder: &mut DisplayListBuilder,
+ info: &DisplayListBuildingInfo) {
debug!("Flow: building display list");
- index = match self.class() {
- BlockFlowClass => self.as_block().build_display_list_block(builder,
- container_block_size,
- absolute_cb_abs_position,
- dirty,
- index,
- lists),
- InlineFlowClass => self.as_inline().build_display_list_inline(builder, container_block_size, dirty, index, lists),
- TableWrapperFlowClass => self.as_table_wrapper().build_display_list_table_wrapper(builder,
- container_block_size,
- absolute_cb_abs_position,
- dirty,
- index,
- lists),
- TableFlowClass => self.as_table().build_display_list_table(builder,
- container_block_size,
- absolute_cb_abs_position,
- dirty,
- index,
- lists),
- TableRowGroupFlowClass => self.as_table_rowgroup().build_display_list_table_rowgroup(builder,
- container_block_size,
- absolute_cb_abs_position,
- dirty,
- index,
- lists),
- TableRowFlowClass => self.as_table_row().build_display_list_table_row(builder,
- container_block_size,
- absolute_cb_abs_position,
- dirty,
- index,
- lists),
- TableCaptionFlowClass => self.as_table_caption().build_display_list_table_caption(builder,
- container_block_size,
- absolute_cb_abs_position,
- dirty,
- index,
- lists),
- TableCellFlowClass => self.as_table_cell().build_display_list_table_cell(builder,
- container_block_size,
- absolute_cb_abs_position,
- dirty,
- index,
- lists),
- TableColGroupFlowClass => index,
- };
-
- if lists.with_mut(|lists| lists.lists[index].list.len() == 0) {
- return true;
- }
-
- if self.is_block_container() || self.is_table_kind() {
- let block = self.as_block();
- let mut child_lists = DisplayListCollection::new();
- child_lists.add_list(DisplayList::new());
- let child_lists = RefCell::new(child_lists);
- let container_block_size;
- let abs_cb_position;
- // TODO(pradeep): Move this into a generated CB function and stuff in Flow.
- match block.box_ {
- Some(ref box_) => {
- // The Containing Block formed by a Block for relatively
- // positioned descendants is the content box.
- container_block_size = box_.content_box_size();
-
- abs_cb_position = if block.is_positioned() {
- block.base.abs_position + block.generated_cb_position()
- } else {
- absolute_cb_abs_position
- };
- }
- None => fail!("Flow: block container should have a box_")
+ match self.class() {
+ BlockFlowClass => {
+ self.as_block().build_display_list_block(stacking_context, builder, info)
}
-
- for kid in block.base.child_iter() {
- if kid.is_absolutely_positioned() {
- // All absolute flows will be handled by their CB.
- continue;
- }
- kid.build_display_lists(builder, &container_block_size,
- abs_cb_position,
- dirty, 0u, &child_lists);
+ InlineFlowClass => {
+ self.as_inline().build_display_list_inline(stacking_context, builder, info)
}
-
- // TODO: Maybe we should handle position 'absolute' and 'fixed'
- // descendants before normal descendants just in case there is a
- // problem when display-list building is parallel and both the
- // original parent and this flow access the same absolute flow.
- // Note that this can only be done once we have paint order
- // working cos currently the later boxes paint over the absolute
- // and fixed boxes :|
- for abs_descendant_link in block.base.abs_descendants.iter() {
- match abs_descendant_link.resolve() {
- Some(flow) => {
- // TODO(pradeep): Send in your abs_position directly.
- flow.build_display_lists(builder, &container_block_size,
- abs_cb_position,
- dirty, 0u, &child_lists);
- }
- None => fail!("empty Rawlink to a descendant")
- }
+ TableWrapperFlowClass => {
+ self.as_table_wrapper().build_display_list_table_wrapper(stacking_context,
+ builder,
+ info)
}
-
- if block.is_root() {
- for fixed_descendant_link in block.base.fixed_descendants.iter() {
- match fixed_descendant_link.resolve() {
- Some(flow) => {
- flow.build_display_lists(builder, &container_block_size,
- abs_cb_position,
- dirty, 0u, &child_lists);
- }
- None => fail!("empty Rawlink to a descendant")
- }
- }
+ TableFlowClass => {
+ self.as_table().build_display_list_table(stacking_context, builder, info)
+ }
+ TableRowGroupFlowClass => {
+ self.as_table_rowgroup().build_display_list_table_rowgroup(stacking_context,
+ builder,
+ info)
+ }
+ TableRowFlowClass => {
+ self.as_table_row().build_display_list_table_row(stacking_context, builder, info)
+ }
+ TableCaptionFlowClass => {
+ self.as_table_caption().build_display_list_table_caption(stacking_context,
+ builder,
+ info)
+ }
+ TableCellFlowClass => {
+ self.as_table_cell().build_display_list_table_cell(stacking_context, builder, info)
+ }
+ TableColGroupFlowClass => {
+ // Nothing to do here, as column groups don't render.
}
-
- let mut child_lists = Some(child_lists.unwrap());
- // Find parent ClipDisplayItemClass and push all child display items
- // under it
- lists.with_mut(|lists| {
- let mut child_lists = child_lists.take_unwrap();
- let result = lists.lists[index].list.mut_rev_iter().position(|item| {
- match *item {
- ClipDisplayItemClass(ref mut item) => {
- item.child_list.push_all_move(child_lists.lists.shift().unwrap().list);
- true
- },
- _ => false,
- }
- });
-
- if result.is_none() {
- fail!("fail to find parent item");
- }
-
- lists.lists.push_all_move(child_lists.lists);
- });
}
- true
}
/// Destroys the flow.
@@ -1314,29 +1227,6 @@ impl MutableOwnedFlowUtils for ~Flow {
}
}
- /// Set fixed descendants for this flow.
- ///
- /// Set yourself as the Containing Block for all the fixed descendants.
- ///
- /// Assumption: This is called in a bottom-up traversal, so that nothing
- /// else is accessing the descendant flows.
- /// Assumption: This is the root flow.
- fn set_fixed_descendants(&mut self, fixed_descendants: FixedDescendants) {
- let self_link = Rawlink::some(*self);
- let block = self.as_block();
- block.base.fixed_descendants = fixed_descendants;
-
- for descendant_link in block.base.fixed_descendants.iter() {
- match descendant_link.resolve() {
- Some(flow) => {
- let base = mut_base(flow);
- base.absolute_cb = self_link.clone();
- }
- None => fail!("empty Rawlink to a descendant")
- }
- }
- }
-
/// Destroys the flow.
fn destroy(&mut self) {
let self_borrowed: &mut Flow = *self;
diff --git a/src/components/main/layout/inline.rs b/src/components/main/layout/inline.rs
index dc05d359ed7..4c3e10fc265 100644
--- a/src/components/main/layout/inline.rs
+++ b/src/components/main/layout/inline.rs
@@ -3,23 +3,24 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use css::node_style::StyledNode;
-use layout::box_::{Box, CannotSplit, GenericBox, IframeBox, ImageBox, ScannedTextBox, SplitDidFit};
-use layout::box_::{SplitDidNotFit, UnscannedTextBox, InlineInfo};
-use layout::box_::{TableColumnBox, TableRowBox, TableWrapperBox, TableCellBox, TableBox};
+use layout::box_::{Box, CannotSplit, GenericBox, IframeBox, ImageBox, InlineInfo, ScannedTextBox};
+use layout::box_::{SplitDidFit, SplitDidNotFit, TableBox, TableCellBox, TableColumnBox};
+use layout::box_::{TableRowBox, TableWrapperBox, UnscannedTextBox};
use layout::context::LayoutContext;
-use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData};
+use layout::display_list_builder::{DisplayListBuilder, DisplayListBuildingInfo};
use layout::floats::{FloatLeft, Floats, PlacementInfo};
use layout::flow::{BaseFlow, FlowClass, Flow, InlineFlowClass};
use layout::flow;
+use layout::model::IntrinsicWidths;
use layout::util::ElementMapping;
use layout::wrapper::ThreadSafeLayoutNode;
use collections::{Deque, RingBuf};
use geom::{Point2D, Rect, Size2D};
-use gfx::display_list::DisplayListCollection;
+use gfx::display_list::{ContentLevel, StackingContext};
use servo_util::geometry::Au;
+use servo_util::geometry;
use servo_util::range::Range;
-use std::cell::RefCell;
use std::mem;
use std::u16;
use style::computed_values::{text_align, vertical_align, white_space};
@@ -480,17 +481,13 @@ impl InlineFlow {
self.boxes = ~[];
}
- pub fn build_display_list_inline<E:ExtraDisplayListData>(
- &self,
+ pub fn build_display_list_inline(&self,
+ stacking_context: &mut StackingContext,
builder: &DisplayListBuilder,
- container_block_size: &Size2D<Au>,
- dirty: &Rect<Au>,
- index: uint,
- lists: &RefCell<DisplayListCollection<E>>)
- -> uint {
+ info: &DisplayListBuildingInfo) {
let abs_rect = Rect(self.base.abs_position, self.base.position.size);
- if !abs_rect.intersects(dirty) {
- return index;
+ if !abs_rect.intersects(&builder.dirty) {
+ return
}
// TODO(#228): Once we form line boxes and have their cached bounds, we can be smarter and
@@ -498,15 +495,19 @@ impl InlineFlow {
debug!("Flow: building display list for {:u} inline boxes", self.boxes.len());
for box_ in self.boxes.iter() {
- let rel_offset: Point2D<Au> = box_.relative_position(container_block_size);
- box_.build_display_list(builder, dirty, self.base.abs_position + rel_offset, (&*self) as &Flow, index, lists);
+ let rel_offset = box_.relative_position(&info.relative_containing_block_size);
+ box_.build_display_list(stacking_context,
+ builder,
+ info,
+ self.base.abs_position + rel_offset,
+ (&*self) as &Flow,
+ ContentLevel);
}
// TODO(#225): Should `inline-block` elements have flows as children of the inline flow or
// should the flow be nested inside the box somehow?
- // For now, don't traverse the subtree rooted here
- index
+ // For now, don't traverse the subtree rooted here.
}
/// Returns the relative offset from the baseline for this box, taking into account the value
@@ -628,20 +629,19 @@ impl Flow for InlineFlow {
child_base.floats = Floats::new();
}
- let mut min_width = Au::new(0);
- let mut pref_width = Au::new(0);
-
+ let mut intrinsic_widths = IntrinsicWidths::new();
for box_ in self.boxes.iter() {
debug!("Flow: measuring {:s}", box_.debug_str());
box_.compute_borders(box_.style());
- let (this_minimum_width, this_preferred_width) =
- box_.minimum_and_preferred_widths();
- min_width = Au::max(min_width, this_minimum_width);
- pref_width = Au::max(pref_width, this_preferred_width);
+
+ let box_intrinsic_widths = box_.intrinsic_widths();
+ intrinsic_widths.minimum_width = geometry::max(intrinsic_widths.minimum_width,
+ box_intrinsic_widths.minimum_width);
+ intrinsic_widths.preferred_width = geometry::max(intrinsic_widths.preferred_width,
+ box_intrinsic_widths.preferred_width);
}
- self.base.min_width = min_width;
- self.base.pref_width = pref_width;
+ self.base.intrinsic_widths = intrinsic_widths;
self.base.num_floats = num_floats;
}
@@ -696,6 +696,7 @@ impl Flow for InlineFlow {
//
// TODO(pcwalton): Cache the linebox scanner?
debug!("assign_height_inline: floats in: {:?}", self.base.floats);
+
// assign height for inline boxes
for box_ in self.boxes.iter() {
box_.assign_replaced_height_if_necessary();
@@ -883,20 +884,6 @@ impl Flow for InlineFlow {
self.base.floats.translate(Point2D(Au::new(0), -self.base.position.size.height));
}
- fn collapse_margins(&mut self,
- _: bool,
- _: &mut bool,
- _: &mut Au,
- _: &mut Au,
- collapsing: &mut Au,
- collapsible: &mut Au) {
- *collapsing = Au::new(0);
- // Non-empty inline flows prevent collapsing between the previous margion and the next.
- if self.base.position.size.height > Au::new(0) {
- *collapsible = Au::new(0);
- }
- }
-
fn debug_str(&self) -> ~str {
~"InlineFlow: " + self.boxes.map(|s| s.debug_str()).connect(", ")
}
diff --git a/src/components/main/layout/layout_task.rs b/src/components/main/layout/layout_task.rs
index b9050816668..267da50e62b 100644
--- a/src/components/main/layout/layout_task.rs
+++ b/src/components/main/layout/layout_task.rs
@@ -11,22 +11,22 @@ use css::select::new_stylist;
use css::node_style::StyledNode;
use layout::construct::{FlowConstructionResult, NoConstructionResult};
use layout::context::LayoutContext;
-use layout::display_list_builder::{DisplayListBuilder, ToGfxColor};
+use layout::display_list_builder::{DisplayListBuilder, DisplayListBuildingInfo, ToGfxColor};
use layout::flow::{Flow, ImmutableFlowUtils, MutableFlowUtils, MutableOwnedFlowUtils};
use layout::flow::{PreorderFlowTraversal, PostorderFlowTraversal};
use layout::flow;
use layout::incremental::RestyleDamage;
use layout::parallel::PaddedUnsafeFlow;
use layout::parallel;
-use layout::util::{LayoutDataAccess, OpaqueNode, LayoutDataWrapper};
+use layout::util::{LayoutDataAccess, LayoutDataWrapper, OpaqueNodeMethods};
use layout::wrapper::{LayoutNode, TLayoutNode, ThreadSafeLayoutNode};
use extra::url::Url;
use geom::point::Point2D;
use geom::rect::Rect;
use geom::size::Size2D;
-use gfx::display_list::{ClipDisplayItemClass, DisplayItem, DisplayItemIterator};
-use gfx::display_list::{DisplayList, DisplayListCollection};
+use gfx::display_list::{ClipDisplayItemClass, DisplayItem, DisplayItemIterator, DisplayList};
+use gfx::display_list::{OpaqueNode, StackingContext};
use gfx::font_context::{FontContext, FontContextInfo};
use gfx::render_task::{RenderMsg, RenderChan, RenderLayer};
use gfx::{render_task, color};
@@ -41,18 +41,20 @@ use script::layout_interface::{ContentChangedDocumentDamage, LayoutChan, Msg, Pr
use script::layout_interface::{QueryMsg, ReapLayoutDataMsg, Reflow, UntrustedNodeAddress};
use script::layout_interface::{ReflowForDisplay, ReflowMsg};
use script::script_task::{ReflowCompleteMsg, ScriptChan, SendEventMsg};
+use servo_msg::compositor_msg::Scrollable;
use servo_msg::constellation_msg::{ConstellationChan, PipelineId, Failure, FailureMsg};
use servo_net::image_cache_task::{ImageCacheTask, ImageResponseMsg};
use servo_net::local_image_cache::{ImageResponder, LocalImageCache};
use servo_util::geometry::Au;
+use servo_util::geometry;
use servo_util::opts::Opts;
+use servo_util::smallvec::{SmallVec, SmallVec0, SmallVec1};
use servo_util::time::{ProfilerChan, profile};
use servo_util::time;
use servo_util::task::send_on_failure;
use servo_util::workqueue::WorkQueue;
use std::cast::transmute;
use std::cast;
-use std::cell::RefCell;
use std::comm::Port;
use std::mem;
use std::ptr;
@@ -79,7 +81,7 @@ pub struct LayoutTask {
script_chan: ScriptChan,
/// The channel on which messages can be sent to the painting task.
- render_chan: RenderChan<OpaqueNode>,
+ render_chan: RenderChan,
/// The channel on which messages can be sent to the image cache.
image_cache_task: ImageCacheTask,
@@ -91,7 +93,7 @@ pub struct LayoutTask {
screen_size: Size2D<Au>,
/// A cached display list.
- display_list_collection: Option<Arc<DisplayListCollection<OpaqueNode>>>,
+ display_list: Option<Arc<DisplayList>>,
stylist: ~Stylist,
@@ -251,7 +253,7 @@ impl LayoutTask {
constellation_chan: ConstellationChan,
failure_msg: Failure,
script_chan: ScriptChan,
- render_chan: RenderChan<OpaqueNode>,
+ render_chan: RenderChan,
img_cache_task: ImageCacheTask,
opts: Opts,
profiler_chan: ProfilerChan,
@@ -282,7 +284,7 @@ impl LayoutTask {
chan: LayoutChan,
constellation_chan: ConstellationChan,
script_chan: ScriptChan,
- render_chan: RenderChan<OpaqueNode>,
+ render_chan: RenderChan,
image_cache_task: ImageCacheTask,
opts: &Opts,
profiler_chan: ProfilerChan)
@@ -306,7 +308,7 @@ impl LayoutTask {
local_image_cache: local_image_cache,
screen_size: screen_size,
- display_list_collection: None,
+ display_list: None,
stylist: ~new_stylist(),
initial_css_values: Arc::new(style::initial_values()),
parallel_traversal: parallel_traversal,
@@ -339,7 +341,7 @@ impl LayoutTask {
stylist: &*self.stylist,
initial_css_values: self.initial_css_values.clone(),
url: (*url).clone(),
- reflow_root: OpaqueNode::from_layout_node(reflow_root),
+ reflow_root: OpaqueNodeMethods::from_layout_node(reflow_root),
opts: self.opts.clone(),
}
}
@@ -432,15 +434,13 @@ impl LayoutTask {
None => fail!("no layout data for root node"),
};
let mut flow = match result {
- FlowConstructionResult(mut flow, abs_descendants, fixed_descendants) => {
+ FlowConstructionResult(mut flow, abs_descendants) => {
// Note: Assuming that the root has display 'static' (as per
// CSS Section 9.3.1). Otherwise, if it were absolutely
// positioned, it would return a reference to itself in
// `abs_descendants` and would lead to a circular reference.
// Set Root as CB for any remaining absolute descendants.
flow.set_abs_descendants(abs_descendants);
- // Set Root as CB for all fixed descendants.
- flow.set_fixed_descendants(fixed_descendants);
flow
}
_ => fail!("Flow construction didn't result in a flow at the root of the tree!"),
@@ -627,26 +627,30 @@ impl LayoutTask {
// Build the display list if necessary, and send it to the renderer.
if data.goal == ReflowForDisplay {
profile(time::LayoutDispListBuildCategory, self.profiler_chan.clone(), || {
- let root_size = flow::base(layout_root).position.size;
- let root_abs_position = Point2D(Au::new(0), Au::new(0));
- let mut display_list_collection = DisplayListCollection::new();
- display_list_collection.add_list(DisplayList::<OpaqueNode>::new());
- let display_list_collection = ~RefCell::new(display_list_collection);
- let dirty = flow::base(layout_root).position.clone();
- let display_list_builder = DisplayListBuilder {
+ let mut root_stacking_context = StackingContext::new();
+ let mut display_list_builder = DisplayListBuilder {
ctx: &layout_ctx,
+ layers: SmallVec0::new(),
+ dirty: flow::base(layout_root).position.clone(),
+ };
+ let display_list_building_info = DisplayListBuildingInfo {
+ relative_containing_block_size: flow::base(layout_root).position.size,
+ absolute_containing_block_position: Point2D(Au(0), Au(0)),
+ layers_needed_for_positioned_flows: false,
};
- layout_root.build_display_lists(&display_list_builder, &root_size,
- root_abs_position,
- &dirty, 0u, display_list_collection);
- let display_list_collection = Arc::new(display_list_collection.unwrap());
+ layout_root.build_display_list(&mut root_stacking_context,
+ &mut display_list_builder,
+ &display_list_building_info);
- let mut color = color::rgba(255.0, 255.0, 255.0, 255.0);
+ let display_list = Arc::new(root_stacking_context.flatten());
+ // FIXME(pcwalton): This is really ugly and can't handle overflow: scroll. Refactor
+ // it with extreme prejudice.
+ let mut color = color::rgba(255.0, 255.0, 255.0, 255.0);
for child in node.traverse_preorder() {
- if child.type_id() == ElementNodeTypeId(HTMLHtmlElementTypeId) ||
- child.type_id() == ElementNodeTypeId(HTMLBodyElementTypeId) {
+ if child.type_id() == Some(ElementNodeTypeId(HTMLHtmlElementTypeId)) ||
+ child.type_id() == Some(ElementNodeTypeId(HTMLBodyElementTypeId)) {
let element_bg_color = {
let thread_safe_child = ThreadSafeLayoutNode::new(&child);
thread_safe_child.style()
@@ -668,18 +672,33 @@ impl LayoutTask {
}
}
+ let root_size = Size2D(display_list_building_info.relative_containing_block_size
+ .width
+ .to_nearest_px() as uint,
+ display_list_building_info.relative_containing_block_size
+ .height
+ .to_nearest_px() as uint);
let render_layer = RenderLayer {
- display_list_collection: display_list_collection.clone(),
- size: Size2D(root_size.width.to_nearest_px() as uint,
- root_size.height.to_nearest_px() as uint),
- color: color
+ id: layout_root.layer_id(0),
+ display_list: display_list.clone(),
+ position: Rect(Point2D(0u, 0u), root_size),
+ background_color: color,
+ scroll_policy: Scrollable,
};
- self.display_list_collection = Some(display_list_collection.clone());
+ self.display_list = Some(display_list.clone());
+
+ let mut layers = SmallVec1::new();
+ layers.push(render_layer);
+ let DisplayListBuilder {
+ layers: sublayers,
+ ..
+ } = display_list_builder;
+ layers.push_all_move(sublayers);
debug!("Layout done!");
- self.render_chan.send(RenderMsg(render_layer));
+ self.render_chan.send(RenderMsg(layers));
});
}
@@ -701,15 +720,14 @@ impl LayoutTask {
// The neat thing here is that in order to answer the following two queries we only
// need to compare nodes for equality. Thus we can safely work only with `OpaqueNode`.
ContentBoxQuery(node, reply_chan) => {
- let node = OpaqueNode::from_script_node(node);
+ let node: OpaqueNode = OpaqueNodeMethods::from_script_node(node);
- fn union_boxes_for_node<'a>(
- accumulator: &mut Option<Rect<Au>>,
- mut iter: DisplayItemIterator<'a,OpaqueNode>,
+ fn union_boxes_for_node(accumulator: &mut Option<Rect<Au>>,
+ mut iter: DisplayItemIterator,
node: OpaqueNode) {
for item in iter {
union_boxes_for_node(accumulator, item.children(), node);
- if item.base().extra == node {
+ if item.base().node == node {
match *accumulator {
None => *accumulator = Some(item.base().bounds),
Some(ref mut acc) => *acc = acc.union(&item.base().bounds),
@@ -719,41 +737,49 @@ impl LayoutTask {
}
let mut rect = None;
- for display_list in self.display_list_collection.as_ref().unwrap().get().iter() {
- union_boxes_for_node(&mut rect, display_list.iter(), node);
+ match self.display_list {
+ None => fail!("no display list!"),
+ Some(ref display_list) => {
+ union_boxes_for_node(&mut rect, display_list.get().iter(), node)
+ }
}
reply_chan.send(ContentBoxResponse(rect.unwrap_or(Au::zero_rect())))
}
ContentBoxesQuery(node, reply_chan) => {
- let node = OpaqueNode::from_script_node(node);
+ let node: OpaqueNode = OpaqueNodeMethods::from_script_node(node);
- fn add_boxes_for_node<'a>(
- accumulator: &mut ~[Rect<Au>],
- mut iter: DisplayItemIterator<'a,OpaqueNode>,
+ fn add_boxes_for_node(accumulator: &mut ~[Rect<Au>],
+ mut iter: DisplayItemIterator,
node: OpaqueNode) {
for item in iter {
add_boxes_for_node(accumulator, item.children(), node);
- if item.base().extra == node {
+ if item.base().node == node {
accumulator.push(item.base().bounds)
}
}
}
let mut boxes = ~[];
- for display_list in self.display_list_collection.as_ref().unwrap().get().iter() {
- add_boxes_for_node(&mut boxes, display_list.iter(), node);
+ match self.display_list {
+ None => fail!("no display list!"),
+ Some(ref display_list) => {
+ add_boxes_for_node(&mut boxes, display_list.get().iter(), node)
+ }
}
reply_chan.send(ContentBoxesResponse(boxes))
}
HitTestQuery(_, point, reply_chan) => {
- fn hit_test(x: Au, y: Au, list: &[DisplayItem<OpaqueNode>])
+ fn hit_test(x: Au, y: Au, list: &[DisplayItem])
-> Option<HitTestResponse> {
for item in list.rev_iter() {
match *item {
ClipDisplayItemClass(ref cc) => {
- let ret = hit_test(x, y, cc.child_list);
- if !ret.is_none() {
- return ret;
+ if !cc.need_clip || geometry::rect_contains_point(cc.base.bounds,
+ Point2D(x, y)) {
+ let ret = hit_test(x, y, cc.child_list.as_slice());
+ if !ret.is_none() {
+ return ret
+ }
}
}
_ => {}
@@ -774,31 +800,35 @@ impl LayoutTask {
y < bounds.origin.y + bounds.size.height &&
bounds.origin.y <= y {
return Some(HitTestResponse(item.base()
- .extra
+ .node
.to_untrusted_node_address()))
}
}
let ret: Option<HitTestResponse> = None;
ret
}
- for display_list in self.display_list_collection.as_ref().unwrap().get().lists.rev_iter() {
- let (x, y) = (Au::from_frac_px(point.x as f64),
- Au::from_frac_px(point.y as f64));
- let resp = hit_test(x,y,display_list.list);
- if resp.is_some() {
- reply_chan.send(Ok(resp.unwrap()));
- return
- }
+ let (x, y) = (Au::from_frac_px(point.x as f64),
+ Au::from_frac_px(point.y as f64));
+ let resp = match self.display_list {
+ None => fail!("no display list!"),
+ Some(ref display_list) => hit_test(x, y, display_list.get().list.as_slice()),
+ };
+ if resp.is_some() {
+ reply_chan.send(Ok(resp.unwrap()));
+ return
}
reply_chan.send(Err(()));
}
MouseOverQuery(_, point, reply_chan) => {
- fn mouse_over_test(x: Au, y: Au, list: &[DisplayItem<OpaqueNode>], result: &mut ~[UntrustedNodeAddress]) {
+ fn mouse_over_test(x: Au,
+ y: Au,
+ list: &[DisplayItem],
+ result: &mut ~[UntrustedNodeAddress]) {
for item in list.rev_iter() {
match *item {
ClipDisplayItemClass(ref cc) => {
- mouse_over_test(x, y, cc.child_list, result);
+ mouse_over_test(x, y, cc.child_list.as_slice(), result);
}
_ => {}
}
@@ -814,18 +844,24 @@ impl LayoutTask {
y < bounds.origin.y + bounds.size.height &&
bounds.origin.y <= y {
result.push(item.base()
- .extra
+ .node
.to_untrusted_node_address());
}
}
}
let mut mouse_over_list:~[UntrustedNodeAddress] = ~[];
- for display_list in self.display_list_collection.as_ref().unwrap().get().lists.rev_iter() {
- let (x, y) = (Au::from_frac_px(point.x as f64),
- Au::from_frac_px(point.y as f64));
- mouse_over_test(x,y,display_list.list, &mut mouse_over_list);
- }
+ let (x, y) = (Au::from_frac_px(point.x as f64),
+ Au::from_frac_px(point.y as f64));
+ match self.display_list {
+ None => fail!("no display list!"),
+ Some(ref display_list) => {
+ mouse_over_test(x,
+ y,
+ display_list.get().list.as_slice(),
+ &mut mouse_over_list);
+ }
+ };
if mouse_over_list.is_empty() {
reply_chan.send(Err(()));
diff --git a/src/components/main/layout/model.rs b/src/components/main/layout/model.rs
index 2a8b6af2f69..378c43d1bb6 100644
--- a/src/components/main/layout/model.rs
+++ b/src/components/main/layout/model.rs
@@ -4,8 +4,257 @@
//! Borders, padding, and margins.
-use servo_util::geometry::Au;
+use layout::box_::Box;
+
use computed = style::computed_values;
+use style::computed_values::{LPA_Auto, LPA_Length, LPA_Percentage};
+use servo_util::geometry::Au;
+use servo_util::geometry;
+
+/// A collapsible margin. See CSS 2.1 § 8.3.1.
+pub struct AdjoiningMargins {
+ /// The value of the greatest positive margin.
+ most_positive: Au,
+
+ /// The actual value (not the absolute value) of the negative margin with the largest absolute
+ /// value. Since this is not the absolute value, this is always zero or negative.
+ most_negative: Au,
+}
+
+impl AdjoiningMargins {
+ pub fn new() -> AdjoiningMargins {
+ AdjoiningMargins {
+ most_positive: Au(0),
+ most_negative: Au(0),
+ }
+ }
+
+ pub fn from_margin(margin_value: Au) -> AdjoiningMargins {
+ if margin_value >= Au(0) {
+ AdjoiningMargins {
+ most_positive: margin_value,
+ most_negative: Au(0),
+ }
+ } else {
+ AdjoiningMargins {
+ most_positive: Au(0),
+ most_negative: margin_value,
+ }
+ }
+ }
+
+ pub fn union(&mut self, other: AdjoiningMargins) {
+ self.most_positive = geometry::max(self.most_positive, other.most_positive);
+ self.most_negative = geometry::min(self.most_negative, other.most_negative)
+ }
+
+ pub fn collapse(&self) -> Au {
+ self.most_positive + self.most_negative
+ }
+}
+
+/// Represents the top and bottom margins of a flow with collapsible margins. See CSS 2.1 § 8.3.1.
+pub enum CollapsibleMargins {
+ /// Margins may not collapse with this flow.
+ NoCollapsibleMargins(Au, Au),
+
+ /// Both the top and bottom margins (specified here in that order) may collapse, but the
+ /// margins do not collapse through this flow.
+ MarginsCollapse(AdjoiningMargins, AdjoiningMargins),
+
+ /// Margins collapse *through* this flow. This means, essentially, that the flow doesn’t
+ /// have any border, padding, or out-of-flow (floating or positioned) content
+ MarginsCollapseThrough(AdjoiningMargins),
+}
+
+impl CollapsibleMargins {
+ pub fn new() -> CollapsibleMargins {
+ NoCollapsibleMargins(Au(0), Au(0))
+ }
+}
+
+enum FinalMarginState {
+ MarginsCollapseThroughFinalMarginState,
+ BottomMarginCollapsesFinalMarginState,
+}
+
+pub struct MarginCollapseInfo {
+ state: MarginCollapseState,
+ top_margin: AdjoiningMargins,
+ margin_in: AdjoiningMargins,
+}
+
+impl MarginCollapseInfo {
+ /// TODO(#2012, pcwalton): Remove this method once `box_` is not an `Option`.
+ pub fn new() -> MarginCollapseInfo {
+ MarginCollapseInfo {
+ state: AccumulatingCollapsibleTopMargin,
+ top_margin: AdjoiningMargins::new(),
+ margin_in: AdjoiningMargins::new(),
+ }
+ }
+
+ pub fn initialize_top_margin(&mut self,
+ fragment: &Box,
+ can_collapse_top_margin_with_kids: bool) {
+ if !can_collapse_top_margin_with_kids {
+ self.state = AccumulatingMarginIn
+ }
+
+ self.top_margin = AdjoiningMargins::from_margin(fragment.margin.get().top)
+ }
+
+ pub fn finish_and_compute_collapsible_margins(mut self,
+ fragment: &Box,
+ can_collapse_bottom_margin_with_kids: bool)
+ -> (CollapsibleMargins, Au) {
+ let state = match self.state {
+ AccumulatingCollapsibleTopMargin => {
+ match fragment.style().Box.get().height {
+ LPA_Auto | LPA_Length(Au(0)) | LPA_Percentage(0.) => {
+ MarginsCollapseThroughFinalMarginState
+ },
+ _ => {
+ // If the box has an explicitly specified height, margins may not collapse
+ // through it.
+ BottomMarginCollapsesFinalMarginState
+ }
+ }
+ }
+ AccumulatingMarginIn => BottomMarginCollapsesFinalMarginState,
+ };
+
+ // Different logic is needed here depending on whether this flow can collapse its bottom
+ // margin with its children.
+ let bottom_margin = fragment.margin.get().bottom;
+ if !can_collapse_bottom_margin_with_kids {
+ match state {
+ MarginsCollapseThroughFinalMarginState => {
+ let advance = self.top_margin.collapse();
+ self.margin_in.union(AdjoiningMargins::from_margin(bottom_margin));
+ (MarginsCollapse(self.top_margin, self.margin_in), advance)
+ }
+ BottomMarginCollapsesFinalMarginState => {
+ let advance = self.margin_in.collapse();
+ self.margin_in.union(AdjoiningMargins::from_margin(bottom_margin));
+ (MarginsCollapse(self.top_margin, self.margin_in), advance)
+ }
+ }
+ } else {
+ match state {
+ MarginsCollapseThroughFinalMarginState => {
+ self.top_margin.union(AdjoiningMargins::from_margin(bottom_margin));
+ (MarginsCollapseThrough(self.top_margin), Au(0))
+ }
+ BottomMarginCollapsesFinalMarginState => {
+ self.margin_in.union(AdjoiningMargins::from_margin(bottom_margin));
+ (MarginsCollapse(self.top_margin, self.margin_in), Au(0))
+ }
+ }
+ }
+ }
+
+ pub fn current_float_ceiling(&mut self) -> Au {
+ match self.state {
+ AccumulatingCollapsibleTopMargin => self.top_margin.collapse(),
+ AccumulatingMarginIn => self.margin_in.collapse(),
+ }
+ }
+
+ /// Adds the child's potentially collapsible top margin to the current margin state and
+ /// advances the Y offset by the appropriate amount to handle that margin. Returns the amount
+ /// that should be added to the Y offset during block layout.
+ pub fn advance_top_margin(&mut self, child_collapsible_margins: &CollapsibleMargins) -> Au {
+ match (self.state, *child_collapsible_margins) {
+ (AccumulatingCollapsibleTopMargin, NoCollapsibleMargins(top, _)) => {
+ self.state = AccumulatingMarginIn;
+ top
+ }
+ (AccumulatingCollapsibleTopMargin, MarginsCollapse(top, _)) => {
+ self.top_margin.union(top);
+ self.state = AccumulatingMarginIn;
+ Au(0)
+ }
+ (AccumulatingMarginIn, NoCollapsibleMargins(top, _)) => {
+ let previous_margin_value = self.margin_in.collapse();
+ self.margin_in = AdjoiningMargins::new();
+ previous_margin_value + top
+ }
+ (AccumulatingMarginIn, MarginsCollapse(top, _)) => {
+ self.margin_in.union(top);
+ let margin_value = self.margin_in.collapse();
+ self.margin_in = AdjoiningMargins::new();
+ margin_value
+ }
+ (_, MarginsCollapseThrough(_)) => {
+ // For now, we ignore this; this will be handled by `advance_bottom_margin` below.
+ Au(0)
+ }
+ }
+ }
+
+ /// Adds the child's potentially collapsible bottom margin to the current margin state and
+ /// advances the Y offset by the appropriate amount to handle that margin. Returns the amount
+ /// that should be added to the Y offset during block layout.
+ pub fn advance_bottom_margin(&mut self, child_collapsible_margins: &CollapsibleMargins) -> Au {
+ match (self.state, *child_collapsible_margins) {
+ (AccumulatingCollapsibleTopMargin, NoCollapsibleMargins(..)) |
+ (AccumulatingCollapsibleTopMargin, MarginsCollapse(..)) => {
+ // Can't happen because the state will have been replaced with
+ // `AccumulatingMarginIn` above.
+ fail!("should not be accumulating collapsible top margins anymore!")
+ }
+ (AccumulatingCollapsibleTopMargin, MarginsCollapseThrough(margin)) => {
+ self.top_margin.union(margin);
+ Au(0)
+ }
+ (AccumulatingMarginIn, NoCollapsibleMargins(_, bottom)) => {
+ assert_eq!(self.margin_in.most_positive, Au(0));
+ assert_eq!(self.margin_in.most_negative, Au(0));
+ bottom
+ }
+ (AccumulatingMarginIn, MarginsCollapse(_, bottom)) |
+ (AccumulatingMarginIn, MarginsCollapseThrough(bottom)) => {
+ self.margin_in.union(bottom);
+ Au(0)
+ }
+ }
+ }
+}
+
+enum MarginCollapseState {
+ AccumulatingCollapsibleTopMargin,
+ AccumulatingMarginIn,
+}
+
+/// Intrinsic widths, which consist of minimum and preferred.
+pub struct IntrinsicWidths {
+ /// The *minimum width* of the content.
+ minimum_width: Au,
+ /// The *preferred width* of the content.
+ preferred_width: Au,
+ /// The estimated sum of borders, padding, and margins. Some calculations use this information
+ /// when computing intrinsic widths.
+ surround_width: Au,
+}
+
+impl IntrinsicWidths {
+ pub fn new() -> IntrinsicWidths {
+ IntrinsicWidths {
+ minimum_width: Au(0),
+ preferred_width: Au(0),
+ surround_width: Au(0),
+ }
+ }
+
+ pub fn total_minimum_width(&self) -> Au {
+ self.minimum_width + self.surround_width
+ }
+
+ pub fn total_preferred_width(&self) -> Au {
+ self.preferred_width + self.surround_width
+ }
+}
/// Useful helper data type when computing values for blocks and positioned elements.
pub enum MaybeAuto {
diff --git a/src/components/main/layout/parallel.rs b/src/components/main/layout/parallel.rs
index d81c14d7a06..3967609cf34 100644
--- a/src/components/main/layout/parallel.rs
+++ b/src/components/main/layout/parallel.rs
@@ -14,10 +14,11 @@ use layout::flow::{Flow, PreorderFlowTraversal, PostorderFlowTraversal};
use layout::flow;
use layout::layout_task::{AssignHeightsAndStoreOverflowTraversal, AssignWidthsTraversal};
use layout::layout_task::{BubbleWidthsTraversal};
-use layout::util::{LayoutDataAccess, OpaqueNode};
+use layout::util::{LayoutDataAccess, OpaqueNodeMethods};
use layout::wrapper::{layout_node_to_unsafe_layout_node, LayoutNode, PostorderNodeMutTraversal};
use layout::wrapper::{ThreadSafeLayoutNode, UnsafeLayoutNode};
+use gfx::display_list::OpaqueNode;
use servo_util::time::{ProfilerChan, profile};
use servo_util::time;
use servo_util::workqueue::{WorkQueue, WorkUnit, WorkerProxy};
@@ -244,7 +245,8 @@ fn recalc_style_for_node(unsafe_layout_node: UnsafeLayoutNode,
node.initialize_layout_data(layout_context.layout_chan.clone());
// Get the parent node.
- let parent_opt = if OpaqueNode::from_layout_node(&node) == layout_context.reflow_root {
+ let opaque_node: OpaqueNode = OpaqueNodeMethods::from_layout_node(&node);
+ let parent_opt = if opaque_node == layout_context.reflow_root {
None
} else {
node.parent_node()
@@ -345,7 +347,8 @@ fn construct_flows(mut unsafe_layout_node: UnsafeLayoutNode,
}
// If this is the reflow root, we're done.
- if layout_context.reflow_root == OpaqueNode::from_layout_node(&node) {
+ let opaque_node: OpaqueNode = OpaqueNodeMethods::from_layout_node(&node);
+ if layout_context.reflow_root == opaque_node {
break
}
diff --git a/src/components/main/layout/table.rs b/src/components/main/layout/table.rs
index 55ce4de5844..1a8b38975ba 100644
--- a/src/components/main/layout/table.rs
+++ b/src/components/main/layout/table.rs
@@ -5,22 +5,21 @@
//! CSS table formatting contexts.
use layout::box_::Box;
-use layout::block::BlockFlow;
-use layout::block::{WidthAndMarginsComputer, WidthConstraintInput, WidthConstraintSolution};
+use layout::block::{BlockFlow, MarginsMayNotCollapse, WidthAndMarginsComputer};
+use layout::block::{WidthConstraintInput, WidthConstraintSolution};
use layout::construct::FlowConstructor;
use layout::context::LayoutContext;
-use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData};
+use layout::display_list_builder::{DisplayListBuilder, DisplayListBuildingInfo};
use layout::floats::{FloatKind};
use layout::flow::{TableFlowClass, FlowClass, Flow, ImmutableFlowUtils};
use layout::flow;
use layout::table_wrapper::{TableLayout, FixedLayout, AutoLayout};
use layout::wrapper::ThreadSafeLayoutNode;
-use std::cell::RefCell;
-use style::computed_values::table_layout;
-use geom::{Point2D, Rect, Size2D};
-use gfx::display_list::DisplayListCollection;
+use gfx::display_list::StackingContext;
use servo_util::geometry::Au;
+use servo_util::geometry;
+use style::computed_values::table_layout;
/// A table flow corresponded to the table's internal table box under a table wrapper flow.
/// The properties `position`, `float`, and `margin-*` are used on the table wrapper box,
@@ -31,6 +30,12 @@ pub struct TableFlow {
/// Column widths
col_widths: ~[Au],
+ /// Column min widths.
+ col_min_widths: ~[Au],
+
+ /// Column pref widths.
+ col_pref_widths: ~[Au],
+
/// Table-layout property
table_layout: TableLayout,
}
@@ -49,6 +54,8 @@ impl TableFlow {
TableFlow {
block_flow: block_flow,
col_widths: ~[],
+ col_min_widths: ~[],
+ col_pref_widths: ~[],
table_layout: table_layout
}
}
@@ -66,6 +73,8 @@ impl TableFlow {
TableFlow {
block_flow: block_flow,
col_widths: ~[],
+ col_min_widths: ~[],
+ col_pref_widths: ~[],
table_layout: table_layout
}
}
@@ -84,6 +93,8 @@ impl TableFlow {
TableFlow {
block_flow: block_flow,
col_widths: ~[],
+ col_min_widths: ~[],
+ col_pref_widths: ~[],
table_layout: table_layout
}
}
@@ -91,61 +102,46 @@ impl TableFlow {
pub fn teardown(&mut self) {
self.block_flow.teardown();
self.col_widths = ~[];
+ self.col_min_widths = ~[];
+ self.col_pref_widths = ~[];
+ }
+
+ /// Update the corresponding value of self_widths if a value of kid_widths has larger value
+ /// than one of self_widths.
+ pub fn update_col_widths(self_widths: &mut ~[Au], kid_widths: &~[Au]) -> Au {
+ let mut sum_widths = Au(0);
+ let mut kid_widths_it = kid_widths.iter();
+ for self_width in self_widths.mut_iter() {
+ match kid_widths_it.next() {
+ Some(kid_width) => {
+ if *self_width < *kid_width {
+ *self_width = *kid_width;
+ }
+ },
+ None => {}
+ }
+ sum_widths = sum_widths + *self_width;
+ }
+ sum_widths
}
/// Assign height for table flow.
///
+ /// TODO(#2014, pcwalton): This probably doesn't handle margin collapse right.
+ ///
/// inline(always) because this is only ever called by in-order or non-in-order top-level
/// methods
#[inline(always)]
- fn assign_height_table_base(&mut self, ctx: &mut LayoutContext, inorder: bool) {
-
- let (_, top_offset, bottom_offset, left_offset) = self.block_flow.initialize_offsets(true);
-
- self.block_flow.handle_children_floats_if_necessary(ctx, inorder,
- left_offset, top_offset);
-
- let mut cur_y = top_offset;
- for kid in self.block_flow.base.child_iter() {
- let child_node = flow::mut_base(kid);
- child_node.position.origin.y = cur_y;
- cur_y = cur_y + child_node.position.size.height;
- }
-
- let height = cur_y - top_offset;
-
- let mut noncontent_height = Au::new(0);
- for box_ in self.block_flow.box_.iter() {
- let mut position = box_.border_box.get();
-
- // noncontent_height = border_top/bottom + padding_top/bottom of box
- noncontent_height = box_.noncontent_height();
-
- position.origin.y = Au(0);
- position.size.height = height + noncontent_height;
-
- box_.border_box.set(position);
- }
-
- self.block_flow.base.position.size.height = height + noncontent_height;
-
- self.block_flow.set_floats_out_if_inorder(inorder, height, cur_y,
- top_offset, bottom_offset, left_offset);
+ fn assign_height_table_base(&mut self, layout_context: &mut LayoutContext, inorder: bool) {
+ self.block_flow.assign_height_block_base(layout_context, inorder, MarginsMayNotCollapse);
}
- pub fn build_display_list_table<E:ExtraDisplayListData>(
- &mut self,
- builder: &DisplayListBuilder,
- container_block_size: &Size2D<Au>,
- absolute_cb_abs_position: Point2D<Au>,
- dirty: &Rect<Au>,
- index: uint,
- lists: &RefCell<DisplayListCollection<E>>)
- -> uint {
+ pub fn build_display_list_table(&mut self,
+ stacking_context: &mut StackingContext,
+ builder: &mut DisplayListBuilder,
+ info: &DisplayListBuildingInfo) {
debug!("build_display_list_table: same process as block flow");
- self.block_flow.build_display_list_block(builder, container_block_size,
- absolute_cb_abs_position,
- dirty, index, lists)
+ self.block_flow.build_display_list_block(stacking_context, builder, info);
}
}
@@ -162,54 +158,95 @@ impl Flow for TableFlow {
&mut self.block_flow
}
- /// This function finds the specified column widths from column group and the first row.
- /// Those are used in fixed table layout calculation.
- /* FIXME: automatic table layout calculation */
- fn bubble_widths(&mut self, ctx: &mut LayoutContext) {
+ fn col_widths<'a>(&'a mut self) -> &'a mut ~[Au] {
+ &mut self.col_widths
+ }
+
+ fn col_min_widths<'a>(&'a self) -> &'a ~[Au] {
+ &self.col_min_widths
+ }
+
+ fn col_pref_widths<'a>(&'a self) -> &'a ~[Au] {
+ &self.col_pref_widths
+ }
+
+ /// The specified column widths are set from column group and the first row for the fixed
+ /// table layout calculation.
+ /// The maximum min/pref widths of each column are set from the rows for the automatic
+ /// table layout calculation.
+ fn bubble_widths(&mut self, _: &mut LayoutContext) {
+ let mut min_width = Au(0);
+ let mut pref_width = Au(0);
let mut did_first_row = false;
+ let mut num_floats = 0;
- /* find max width from child block contexts */
for kid in self.block_flow.base.child_iter() {
assert!(kid.is_proper_table_child());
if kid.is_table_colgroup() {
self.col_widths.push_all(kid.as_table_colgroup().widths);
+ self.col_min_widths = self.col_widths.clone();
+ self.col_pref_widths = self.col_widths.clone();
} else if kid.is_table_rowgroup() || kid.is_table_row() {
// read column widths from table-row-group/table-row, and assign
// width=0 for the columns not defined in column-group
// FIXME: need to read widths from either table-header-group OR
// first table-row
- let kid_col_widths = if kid.is_table_rowgroup() {
- &kid.as_table_rowgroup().col_widths
- } else {
- &kid.as_table_row().col_widths
- };
match self.table_layout {
- FixedLayout if !did_first_row => {
- did_first_row = true;
- let mut child_widths = kid_col_widths.iter();
- for col_width in self.col_widths.mut_iter() {
- match child_widths.next() {
- Some(child_width) => {
- if *col_width == Au::new(0) {
- *col_width = *child_width;
- }
- },
- None => break
+ FixedLayout => {
+ let kid_col_widths = kid.col_widths();
+ if !did_first_row {
+ did_first_row = true;
+ let mut child_widths = kid_col_widths.iter();
+ for col_width in self.col_widths.mut_iter() {
+ match child_widths.next() {
+ Some(child_width) => {
+ if *col_width == Au::new(0) {
+ *col_width = *child_width;
+ }
+ },
+ None => break
+ }
}
}
+ let num_child_cols = kid_col_widths.len();
+ let num_cols = self.col_widths.len();
+ debug!("table until the previous row has {} column(s) and this row has {} column(s)",
+ num_cols, num_child_cols);
+ for i in range(num_cols, num_child_cols) {
+ self.col_widths.push( kid_col_widths[i] );
+ }
},
- _ => {}
- }
- let num_child_cols = kid_col_widths.len();
- let num_cols = self.col_widths.len();
- debug!("colgroup has {} column(s) and child has {} column(s)", num_cols, num_child_cols);
- for i in range(num_cols, num_child_cols) {
- self.col_widths.push( kid_col_widths[i] );
+ AutoLayout => {
+ min_width = TableFlow::update_col_widths(&mut self.col_min_widths, kid.col_min_widths());
+ pref_width = TableFlow::update_col_widths(&mut self.col_pref_widths, kid.col_pref_widths());
+
+ // update the number of column widths from table-rows.
+ let num_cols = self.col_min_widths.len();
+ let num_child_cols = kid.col_min_widths().len();
+ debug!("table until the previous row has {} column(s) and this row has {} column(s)",
+ num_cols, num_child_cols);
+ for i in range(num_cols, num_child_cols) {
+ self.col_widths.push(Au::new(0));
+ let new_kid_min = kid.col_min_widths()[i];
+ self.col_min_widths.push( new_kid_min );
+ let new_kid_pref = kid.col_pref_widths()[i];
+ self.col_pref_widths.push( new_kid_pref );
+ min_width = min_width + new_kid_min;
+ pref_width = pref_width + new_kid_pref;
+ }
+ }
}
}
+ let child_base = flow::mut_base(kid);
+ num_floats = num_floats + child_base.num_floats;
+ }
+ for box_ in self.block_flow.box_.iter() {
+ box_.compute_borders(box_.style());
}
- self.block_flow.bubble_widths(ctx);
+ self.block_flow.base.num_floats = num_floats;
+ self.block_flow.base.intrinsic_widths.minimum_width = min_width;
+ self.block_flow.base.intrinsic_widths.preferred_width = geometry::max(min_width, pref_width);
}
/// Recursively (top-down) determines the actual width of child contexts and boxes. When called
@@ -242,20 +279,25 @@ impl Flow for TableFlow {
content_width = box_.border_box.get().size.width - padding_and_borders;
}
- // In fixed table layout, we distribute extra space among the unspecified columns if there are
- // any, or among all the columns if all are specified.
- if (total_column_width < content_width) && (num_unspecified_widths == 0) {
- let ratio = content_width.to_f64().unwrap() / total_column_width.to_f64().unwrap();
- for col_width in self.col_widths.mut_iter() {
- *col_width = (*col_width).scale_by(ratio);
- }
- } else if num_unspecified_widths != 0 {
- let extra_column_width = (content_width - total_column_width) / Au::new(num_unspecified_widths);
- for col_width in self.col_widths.mut_iter() {
- if *col_width == Au(0) {
- *col_width = extra_column_width;
+ match self.table_layout {
+ FixedLayout => {
+ // In fixed table layout, we distribute extra space among the unspecified columns if there are
+ // any, or among all the columns if all are specified.
+ if (total_column_width < content_width) && (num_unspecified_widths == 0) {
+ let ratio = content_width.to_f64().unwrap() / total_column_width.to_f64().unwrap();
+ for col_width in self.col_widths.mut_iter() {
+ *col_width = (*col_width).scale_by(ratio);
+ }
+ } else if num_unspecified_widths != 0 {
+ let extra_column_width = (content_width - total_column_width) / Au::new(num_unspecified_widths);
+ for col_width in self.col_widths.mut_iter() {
+ if *col_width == Au(0) {
+ *col_width = extra_column_width;
+ }
+ }
}
}
+ _ => {}
}
self.block_flow.propagate_assigned_width_to_children(left_content_edge, content_width, Some(self.col_widths.clone()));
@@ -275,20 +317,6 @@ impl Flow for TableFlow {
self.assign_height_table_base(ctx, false);
}
- // CSS Section 8.3.1 - Collapsing Margins
- // Since `margin` is not used on table box, `collapsing` and `collapsible` are set to 0
- fn collapse_margins(&mut self,
- _: bool,
- _: &mut bool,
- _: &mut Au,
- _: &mut Au,
- collapsing: &mut Au,
- collapsible: &mut Au) {
- // `margin` is not used on table box.
- *collapsing = Au::new(0);
- *collapsible = Au::new(0);
- }
-
fn debug_str(&self) -> ~str {
let txt = ~"TableFlow: ";
txt.append(match self.block_flow.box_ {
diff --git a/src/components/main/layout/table_caption.rs b/src/components/main/layout/table_caption.rs
index b9ca7bd211a..7aecbe22fb5 100644
--- a/src/components/main/layout/table_caption.rs
+++ b/src/components/main/layout/table_caption.rs
@@ -7,14 +7,11 @@
use layout::block::BlockFlow;
use layout::construct::FlowConstructor;
use layout::context::LayoutContext;
-use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData};
+use layout::display_list_builder::{DisplayListBuilder, DisplayListBuildingInfo};
use layout::flow::{TableCaptionFlowClass, FlowClass, Flow};
use layout::wrapper::ThreadSafeLayoutNode;
-use std::cell::RefCell;
-use geom::{Point2D, Rect, Size2D};
-use gfx::display_list::DisplayListCollection;
-use servo_util::geometry::Au;
+use gfx::display_list::StackingContext;
/// A table formatting context.
pub struct TableCaptionFlow {
@@ -34,19 +31,12 @@ impl TableCaptionFlow {
self.block_flow.teardown();
}
- pub fn build_display_list_table_caption<E:ExtraDisplayListData>(
- &mut self,
- builder: &DisplayListBuilder,
- container_block_size: &Size2D<Au>,
- absolute_cb_abs_position: Point2D<Au>,
- dirty: &Rect<Au>,
- index: uint,
- lists: &RefCell<DisplayListCollection<E>>)
- -> uint {
+ pub fn build_display_list_table_caption(&mut self,
+ stacking_context: &mut StackingContext,
+ builder: &mut DisplayListBuilder,
+ info: &DisplayListBuildingInfo) {
debug!("build_display_list_table_caption: same process as block flow");
- self.block_flow.build_display_list_block(builder, container_block_size,
- absolute_cb_abs_position,
- dirty, index, lists)
+ self.block_flow.build_display_list_block(stacking_context, builder, info)
}
}
@@ -86,13 +76,6 @@ impl Flow for TableCaptionFlow {
self.block_flow.assign_height(ctx);
}
- /// table-caption has margins but is not collapsed with a sibling(table)
- /// or its parents(table-wrapper).
- /// Therefore, margins to be collapsed do not exist.
- fn collapse_margins(&mut self, _: bool, _: &mut bool, _: &mut Au,
- _: &mut Au, _: &mut Au, _: &mut Au) {
- }
-
fn debug_str(&self) -> ~str {
let txt = ~"TableCaptionFlow: ";
txt.append(match self.block_flow.box_ {
diff --git a/src/components/main/layout/table_cell.rs b/src/components/main/layout/table_cell.rs
index 624f0d8deb4..d6e88b629fe 100644
--- a/src/components/main/layout/table_cell.rs
+++ b/src/components/main/layout/table_cell.rs
@@ -5,16 +5,15 @@
//! CSS table formatting contexts.
use layout::box_::Box;
-use layout::block::{BlockFlow, WidthAndMarginsComputer};
+use layout::block::{BlockFlow, MarginsMayNotCollapse, WidthAndMarginsComputer};
use layout::context::LayoutContext;
-use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData};
+use layout::display_list_builder::{DisplayListBuilder, DisplayListBuildingInfo};
use layout::flow::{TableCellFlowClass, FlowClass, Flow};
+use layout::model::{MaybeAuto};
use layout::table::InternalTable;
use layout::wrapper::ThreadSafeLayoutNode;
-use std::cell::RefCell;
-use geom::{Point2D, Rect, Size2D};
-use gfx::display_list::{DisplayListCollection};
+use gfx::display_list::StackingContext;
use servo_util::geometry::Au;
/// A table formatting context.
@@ -42,62 +41,23 @@ impl TableCellFlow {
/// Assign height for table-cell flow.
///
+ /// TODO(#2015, pcwalton): This doesn't handle floats right.
+ ///
/// inline(always) because this is only ever called by in-order or non-in-order top-level
/// methods
#[inline(always)]
- fn assign_height_table_cell_base(&mut self, ctx: &mut LayoutContext, inorder: bool) {
- let (_, mut top_offset, bottom_offset, left_offset) = self.block_flow
- .initialize_offsets(true);
-
- self.block_flow.handle_children_floats_if_necessary(ctx, inorder,
- left_offset, top_offset);
- let mut cur_y = top_offset;
- // Since table cell does not have `margin`, the first child's top margin and
- // the last child's bottom margin do not collapse.
- self.block_flow.compute_margin_collapse(&mut cur_y,
- &mut top_offset,
- &mut Au(0),
- &mut Au(0),
- false,
- false);
-
- // CSS 2.1 § 17.5.3. Table cell box height is the minimum height required by the content.
- let height = cur_y - top_offset;
-
- // TODO(june0cho): vertical-align of table-cell should be calculated.
- let mut noncontent_height = Au::new(0);
- for box_ in self.block_flow.box_.iter() {
- let mut position = box_.border_box.get();
-
- // noncontent_height = border_top/bottom + padding_top/bottom of box
- noncontent_height = box_.noncontent_height();
-
- position.origin.y = Au(0);
- position.size.height = height + noncontent_height;
-
- box_.border_box.set(position);
- }
-
- self.block_flow.base.position.size.height = height + noncontent_height;
-
- self.block_flow.set_floats_out_if_inorder(inorder, height, cur_y, top_offset,
- bottom_offset, left_offset);
- self.block_flow.assign_height_absolute_flows(ctx);
+ fn assign_height_table_cell_base(&mut self,
+ layout_context: &mut LayoutContext,
+ inorder: bool) {
+ self.block_flow.assign_height_block_base(layout_context, inorder, MarginsMayNotCollapse)
}
- pub fn build_display_list_table_cell<E:ExtraDisplayListData>(
- &mut self,
- builder: &DisplayListBuilder,
- container_block_size: &Size2D<Au>,
- absolute_cb_abs_position: Point2D<Au>,
- dirty: &Rect<Au>,
- index: uint,
- lists: &RefCell<DisplayListCollection<E>>)
- -> uint {
- debug!("build_display_list_table_cell: same process as block flow");
- self.block_flow.build_display_list_block(builder, container_block_size,
- absolute_cb_abs_position,
- dirty, index, lists)
+ pub fn build_display_list_table_cell(&mut self,
+ stacking_context: &mut StackingContext,
+ builder: &mut DisplayListBuilder,
+ info: &DisplayListBuildingInfo) {
+ debug!("build_display_list_table: same process as block flow");
+ self.block_flow.build_display_list_block(stacking_context, builder, info)
}
}
@@ -117,6 +77,18 @@ impl Flow for TableCellFlow {
/// Minimum/preferred widths set by this function are used in automatic table layout calculation.
fn bubble_widths(&mut self, ctx: &mut LayoutContext) {
self.block_flow.bubble_widths(ctx);
+ for box_ in self.block_flow.box_.iter() {
+ let specified_width = MaybeAuto::from_style(box_.style().Box.get().width,
+ Au::new(0)).specified_or_zero();
+ if self.block_flow.base.intrinsic_widths.minimum_width < specified_width {
+ self.block_flow.base.intrinsic_widths.minimum_width = specified_width;
+ }
+ if self.block_flow.base.intrinsic_widths.preferred_width <
+ self.block_flow.base.intrinsic_widths.minimum_width {
+ self.block_flow.base.intrinsic_widths.preferred_width =
+ self.block_flow.base.intrinsic_widths.minimum_width;
+ }
+ }
}
/// Recursively (top-down) determines the actual width of child contexts and boxes. When called
@@ -156,12 +128,6 @@ impl Flow for TableCellFlow {
self.assign_height_table_cell_base(ctx, false);
}
- /// TableCellBox and their parents(TableRowBox) do not have margins.
- /// Therefore, margins to be collapsed do not exist.
- fn collapse_margins(&mut self, _: bool, _: &mut bool, _: &mut Au,
- _: &mut Au, _: &mut Au, _: &mut Au) {
- }
-
fn debug_str(&self) -> ~str {
let txt = ~"TableCellFlow: ";
txt.append(match self.block_flow.box_ {
diff --git a/src/components/main/layout/table_colgroup.rs b/src/components/main/layout/table_colgroup.rs
index 8d9e1d98870..a53c825660f 100644
--- a/src/components/main/layout/table_colgroup.rs
+++ b/src/components/main/layout/table_colgroup.rs
@@ -82,12 +82,6 @@ impl Flow for TableColGroupFlow {
fn assign_height(&mut self, _ctx: &mut LayoutContext) {
}
- /// TableColumnBox and their parents(TableBox) do not have margins.
- /// Therefore, margins to be collapsed do not exist.
- fn collapse_margins(&mut self, _: bool, _: &mut bool, _: &mut Au,
- _: &mut Au, _: &mut Au, _: &mut Au) {
- }
-
fn debug_str(&self) -> ~str {
let txt = ~"TableColGroupFlow: ";
txt.append(match self.box_ {
diff --git a/src/components/main/layout/table_row.rs b/src/components/main/layout/table_row.rs
index 17ebaded351..8dbc40265d0 100644
--- a/src/components/main/layout/table_row.rs
+++ b/src/components/main/layout/table_row.rs
@@ -9,16 +9,14 @@ use layout::block::BlockFlow;
use layout::block::WidthAndMarginsComputer;
use layout::construct::FlowConstructor;
use layout::context::LayoutContext;
-use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData};
+use layout::display_list_builder::{DisplayListBuilder, DisplayListBuildingInfo};
use layout::flow::{TableRowFlowClass, FlowClass, Flow, ImmutableFlowUtils};
use layout::flow;
use layout::table::InternalTable;
use layout::model::{MaybeAuto, Specified, Auto};
use layout::wrapper::ThreadSafeLayoutNode;
-use std::cell::RefCell;
-use geom::{Point2D, Rect, Size2D};
-use gfx::display_list::DisplayListCollection;
+use gfx::display_list::StackingContext;
use servo_util::geometry::Au;
use servo_util::geometry;
@@ -28,6 +26,12 @@ pub struct TableRowFlow {
/// Column widths.
col_widths: ~[Au],
+
+ /// Column min widths.
+ col_min_widths: ~[Au],
+
+ /// Column pref widths.
+ col_pref_widths: ~[Au],
}
impl TableRowFlow {
@@ -37,6 +41,8 @@ impl TableRowFlow {
TableRowFlow {
block_flow: BlockFlow::from_node_and_box(node, box_),
col_widths: ~[],
+ col_min_widths: ~[],
+ col_pref_widths: ~[],
}
}
@@ -46,12 +52,16 @@ impl TableRowFlow {
TableRowFlow {
block_flow: BlockFlow::from_node(constructor, node),
col_widths: ~[],
+ col_min_widths: ~[],
+ col_pref_widths: ~[],
}
}
pub fn teardown(&mut self) {
self.block_flow.teardown();
self.col_widths = ~[];
+ self.col_min_widths = ~[];
+ self.col_pref_widths = ~[];
}
pub fn box_<'a>(&'a mut self) -> &'a Option<Box>{
@@ -66,19 +76,23 @@ impl TableRowFlow {
/// Assign height for table-row flow.
///
+ /// TODO(pcwalton): This doesn't handle floats and positioned elements right.
+ ///
/// inline(always) because this is only ever called by in-order or non-in-order top-level
/// methods
#[inline(always)]
- fn assign_height_table_row_base(&mut self, ctx: &mut LayoutContext, inorder: bool) {
- let (top_offset, bottom_offset, left_offset) = self.initialize_offsets();
+ fn assign_height_table_row_base(&mut self, layout_context: &mut LayoutContext, inorder: bool) {
+ let (top_offset, _, _) = self.initialize_offsets();
- self.block_flow.handle_children_floats_if_necessary(ctx, inorder,
- left_offset, top_offset);
let mut cur_y = top_offset;
// Per CSS 2.1 § 17.5.3, find max_y = max( computed `height`, minimum height of all cells )
let mut max_y = Au::new(0);
for kid in self.block_flow.base.child_iter() {
+ if inorder {
+ kid.assign_height_inorder(layout_context)
+ }
+
for child_box in kid.as_table_cell().box_().iter() {
// TODO: Percentage height
let child_specified_height = MaybeAuto::from_style(child_box.style().Box.get().height,
@@ -101,6 +115,8 @@ impl TableRowFlow {
cur_y = cur_y + height;
// Assign the height of own box
+ //
+ // FIXME(pcwalton): Take `cur_y` into account.
for box_ in self.block_flow.box_.iter() {
let mut position = box_.border_box.get();
position.size.height = height;
@@ -118,24 +134,14 @@ impl TableRowFlow {
let child_node = flow::mut_base(kid);
child_node.position.size.height = height;
}
-
- self.block_flow.set_floats_out_if_inorder(inorder, height, cur_y,
- top_offset, bottom_offset, left_offset);
}
- pub fn build_display_list_table_row<E:ExtraDisplayListData>(
- &mut self,
- builder: &DisplayListBuilder,
- container_block_size: &Size2D<Au>,
- absolute_cb_abs_position: Point2D<Au>,
- dirty: &Rect<Au>,
- index: uint,
- lists: &RefCell<DisplayListCollection<E>>)
- -> uint {
+ pub fn build_display_list_table_row(&mut self,
+ stacking_context: &mut StackingContext,
+ builder: &mut DisplayListBuilder,
+ info: &DisplayListBuildingInfo) {
debug!("build_display_list_table_row: same process as block flow");
- self.block_flow.build_display_list_block(builder, container_block_size,
- absolute_cb_abs_position,
- dirty, index, lists)
+ self.block_flow.build_display_list_block(stacking_context, builder, info)
}
}
@@ -152,27 +158,51 @@ impl Flow for TableRowFlow {
&mut self.block_flow
}
+ fn col_widths<'a>(&'a mut self) -> &'a mut ~[Au] {
+ &mut self.col_widths
+ }
+
+ fn col_min_widths<'a>(&'a self) -> &'a ~[Au] {
+ &self.col_min_widths
+ }
+
+ fn col_pref_widths<'a>(&'a self) -> &'a ~[Au] {
+ &self.col_pref_widths
+ }
+
/// Recursively (bottom-up) determines the context's preferred and minimum widths. When called
/// on this context, all child contexts have had their min/pref widths set. This function must
/// decide min/pref widths based on child context widths and dimensions of any boxes it is
/// responsible for flowing.
/// Min/pref widths set by this function are used in automatic table layout calculation.
- /// Also, this function collects the specified column widths of children cells. Those are used
- /// in fixed table layout calculation
- fn bubble_widths(&mut self, ctx: &mut LayoutContext) {
+ /// The specified column widths of children cells are used in fixed table layout calculation.
+ fn bubble_widths(&mut self, _: &mut LayoutContext) {
+ let mut min_width = Au(0);
+ let mut pref_width = Au(0);
+ let mut num_floats = 0;
/* find the specified widths from child table-cell contexts */
for kid in self.block_flow.base.child_iter() {
assert!(kid.is_table_cell());
+ // collect the specified column widths of cells. These are used in fixed table layout calculation.
for child_box in kid.as_table_cell().box_().iter() {
let child_specified_width = MaybeAuto::from_style(child_box.style().Box.get().width,
Au::new(0)).specified_or_zero();
self.col_widths.push(child_specified_width);
}
- }
- // TODO: calculate min_width & pref_width for automatic table layout calculation
- self.block_flow.bubble_widths(ctx);
+ // collect min_width & pref_width of children cells for automatic table layout calculation.
+ let child_base = flow::mut_base(kid);
+ self.col_min_widths.push(child_base.intrinsic_widths.minimum_width);
+ self.col_pref_widths.push(child_base.intrinsic_widths.preferred_width);
+ min_width = min_width + child_base.intrinsic_widths.minimum_width;
+ pref_width = pref_width + child_base.intrinsic_widths.preferred_width;
+ num_floats = num_floats + child_base.num_floats;
+ }
+ self.block_flow.base.num_floats = num_floats;
+ self.block_flow.base.intrinsic_widths.minimum_width = min_width;
+ self.block_flow.base.intrinsic_widths.preferred_width = geometry::max(min_width,
+ pref_width);
}
/// Recursively (top-down) determines the actual width of child contexts and boxes. When called
@@ -205,12 +235,6 @@ impl Flow for TableRowFlow {
self.assign_height_table_row_base(ctx, false);
}
- /// TableRowBox and their parents(TableBox) do not have margins.
- /// Therefore, margins to be collapsed do not exist.
- fn collapse_margins(&mut self, _: bool, _: &mut bool, _: &mut Au,
- _: &mut Au, _: &mut Au, _: &mut Au) {
- }
-
fn debug_str(&self) -> ~str {
let txt = ~"TableRowFlow: ";
txt.append(match self.block_flow.box_ {
diff --git a/src/components/main/layout/table_rowgroup.rs b/src/components/main/layout/table_rowgroup.rs
index 92bb505d1e9..a0b2364146c 100644
--- a/src/components/main/layout/table_rowgroup.rs
+++ b/src/components/main/layout/table_rowgroup.rs
@@ -9,16 +9,15 @@ use layout::block::BlockFlow;
use layout::block::WidthAndMarginsComputer;
use layout::construct::FlowConstructor;
use layout::context::LayoutContext;
-use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData};
+use layout::display_list_builder::{DisplayListBuilder, DisplayListBuildingInfo};
use layout::flow::{TableRowGroupFlowClass, FlowClass, Flow, ImmutableFlowUtils};
use layout::flow;
-use layout::table::InternalTable;
+use layout::table::{InternalTable, TableFlow};
use layout::wrapper::ThreadSafeLayoutNode;
-use std::cell::RefCell;
-use geom::{Point2D, Rect, Size2D};
-use gfx::display_list::DisplayListCollection;
+use gfx::display_list::StackingContext;
use servo_util::geometry::Au;
+use servo_util::geometry;
/// A table formatting context.
pub struct TableRowGroupFlow {
@@ -26,6 +25,12 @@ pub struct TableRowGroupFlow {
/// Column widths
col_widths: ~[Au],
+
+ /// Column min widths.
+ col_min_widths: ~[Au],
+
+ /// Column pref widths.
+ col_pref_widths: ~[Au],
}
impl TableRowGroupFlow {
@@ -35,6 +40,8 @@ impl TableRowGroupFlow {
TableRowGroupFlow {
block_flow: BlockFlow::from_node_and_box(node, box_),
col_widths: ~[],
+ col_min_widths: ~[],
+ col_pref_widths: ~[],
}
}
@@ -44,12 +51,16 @@ impl TableRowGroupFlow {
TableRowGroupFlow {
block_flow: BlockFlow::from_node(constructor, node),
col_widths: ~[],
+ col_min_widths: ~[],
+ col_pref_widths: ~[],
}
}
pub fn teardown(&mut self) {
self.block_flow.teardown();
self.col_widths = ~[];
+ self.col_min_widths = ~[];
+ self.col_pref_widths = ~[];
}
pub fn box_<'a>(&'a mut self) -> &'a Option<Box>{
@@ -64,14 +75,14 @@ impl TableRowGroupFlow {
/// Assign height for table-rowgroup flow.
///
+ /// FIXME(pcwalton): This doesn't handle floats right.
+ ///
/// inline(always) because this is only ever called by in-order or non-in-order top-level
/// methods
#[inline(always)]
- fn assign_height_table_rowgroup_base(&mut self, ctx: &mut LayoutContext, inorder: bool) {
- let (top_offset, bottom_offset, left_offset) = self.initialize_offsets();
+ fn assign_height_table_rowgroup_base(&mut self, _: &mut LayoutContext, _: bool) {
+ let (top_offset, _, _) = self.initialize_offsets();
- self.block_flow.handle_children_floats_if_necessary(ctx, inorder,
- left_offset, top_offset);
let mut cur_y = top_offset;
for kid in self.block_flow.base.child_iter() {
@@ -88,24 +99,14 @@ impl TableRowGroupFlow {
box_.border_box.set(position);
}
self.block_flow.base.position.size.height = height;
-
- self.block_flow.set_floats_out_if_inorder(inorder, height, cur_y,
- top_offset, bottom_offset, left_offset);
}
- pub fn build_display_list_table_rowgroup<E:ExtraDisplayListData>(
- &mut self,
- builder: &DisplayListBuilder,
- container_block_size: &Size2D<Au>,
- absolute_cb_abs_position: Point2D<Au>,
- dirty: &Rect<Au>,
- index: uint,
- lists: &RefCell<DisplayListCollection<E>>)
- -> uint {
+ pub fn build_display_list_table_rowgroup(&mut self,
+ stacking_context: &mut StackingContext,
+ builder: &mut DisplayListBuilder,
+ info: &DisplayListBuildingInfo) {
debug!("build_display_list_table_rowgroup: same process as block flow");
- self.block_flow.build_display_list_block(builder, container_block_size,
- absolute_cb_abs_position,
- dirty, index, lists)
+ self.block_flow.build_display_list_block(stacking_context, builder, info)
}
}
@@ -122,6 +123,18 @@ impl Flow for TableRowGroupFlow {
&mut self.block_flow
}
+ fn col_widths<'a>(&'a mut self) -> &'a mut ~[Au] {
+ &mut self.col_widths
+ }
+
+ fn col_min_widths<'a>(&'a self) -> &'a ~[Au] {
+ &self.col_min_widths
+ }
+
+ fn col_pref_widths<'a>(&'a self) -> &'a ~[Au] {
+ &self.col_pref_widths
+ }
+
/// Recursively (bottom-up) determines the context's preferred and minimum widths. When called
/// on this context, all child contexts have had their min/pref widths set. This function must
/// decide min/pref widths based on child context widths and dimensions of any boxes it is
@@ -129,24 +142,48 @@ impl Flow for TableRowGroupFlow {
/// Min/pref widths set by this function are used in automatic table layout calculation.
/// Also, this function finds the specified column widths from the first row.
/// Those are used in fixed table layout calculation
- fn bubble_widths(&mut self, ctx: &mut LayoutContext) {
- /* find the specified column widths from the first table-row.
- update the number of column widths from other table-rows. */
+ fn bubble_widths(&mut self, _: &mut LayoutContext) {
+ let mut min_width = Au(0);
+ let mut pref_width = Au(0);
+ let mut num_floats = 0;
+
for kid in self.block_flow.base.child_iter() {
assert!(kid.is_table_row());
- if self.col_widths.is_empty() {
- self.col_widths = kid.as_table_row().col_widths.clone();
+
+ // calculate min_width & pref_width for automatic table layout calculation
+ // 'self.col_min_widths' collects the maximum value of cells' min-widths for each column.
+ // 'self.col_pref_widths' collects the maximum value of cells' pref-widths for each column.
+ if self.col_widths.is_empty() { // First Row
+ assert!(self.col_min_widths.is_empty() && self.col_pref_widths.is_empty());
+ // 'self.col_widths' collects the specified column widths from the first table-row for fixed table layout calculation.
+ self.col_widths = kid.col_widths().clone();
+ self.col_min_widths = kid.col_min_widths().clone();
+ self.col_pref_widths = kid.col_pref_widths().clone();
} else {
+ min_width = TableFlow::update_col_widths(&mut self.col_min_widths, kid.col_min_widths());
+ pref_width = TableFlow::update_col_widths(&mut self.col_pref_widths, kid.col_pref_widths());
+
+ // update the number of column widths from table-rows.
let num_cols = self.col_widths.len();
- let num_child_cols = kid.as_table_row().col_widths.len();
- for _ in range(num_cols, num_child_cols) {
+ let num_child_cols = kid.col_min_widths().len();
+ for i in range(num_cols, num_child_cols) {
self.col_widths.push(Au::new(0));
+ let new_kid_min = kid.col_min_widths()[i];
+ self.col_min_widths.push(kid.col_min_widths()[i]);
+ let new_kid_pref = kid.col_pref_widths()[i];
+ self.col_pref_widths.push(kid.col_pref_widths()[i]);
+ min_width = min_width + new_kid_min;
+ pref_width = pref_width + new_kid_pref;
}
}
+ let child_base = flow::mut_base(kid);
+ num_floats = num_floats + child_base.num_floats;
}
- // TODO: calculate min_width & pref_width for automatic table layout calculation
- self.block_flow.bubble_widths(ctx);
+ self.block_flow.base.num_floats = num_floats;
+ self.block_flow.base.intrinsic_widths.minimum_width = min_width;
+ self.block_flow.base.intrinsic_widths.preferred_width = geometry::max(min_width,
+ pref_width);
}
/// Recursively (top-down) determines the actual width of child contexts and boxes. When called
@@ -180,12 +217,6 @@ impl Flow for TableRowGroupFlow {
self.assign_height_table_rowgroup_base(ctx, false);
}
- /// TableRowBox and their parents(TableBox) do not have margins.
- /// Therefore, margins to be collapsed do not exist.
- fn collapse_margins(&mut self, _: bool, _: &mut bool, _: &mut Au,
- _: &mut Au, _: &mut Au, _: &mut Au) {
- }
-
fn debug_str(&self) -> ~str {
let txt = ~"TableRowGroupFlow: ";
txt.append(match self.block_flow.box_ {
diff --git a/src/components/main/layout/table_wrapper.rs b/src/components/main/layout/table_wrapper.rs
index d0ddfa11f92..639eb542902 100644
--- a/src/components/main/layout/table_wrapper.rs
+++ b/src/components/main/layout/table_wrapper.rs
@@ -5,23 +5,20 @@
//! CSS table formatting contexts.
use layout::box_::Box;
-use layout::block::BlockFlow;
-use layout::block::{WidthAndMarginsComputer, WidthConstraintInput, WidthConstraintSolution};
+use layout::block::{BlockFlow, MarginsMayNotCollapse, WidthAndMarginsComputer};
+use layout::block::{WidthConstraintInput, WidthConstraintSolution};
use layout::construct::FlowConstructor;
use layout::context::LayoutContext;
-use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData};
-use layout::floats::{FloatKind};
+use layout::display_list_builder::{DisplayListBuilder, DisplayListBuildingInfo};
+use layout::floats::FloatKind;
use layout::flow::{TableWrapperFlowClass, FlowClass, Flow, ImmutableFlowUtils};
-use layout::flow;
-use layout::model::{MaybeAuto, Specified, Auto, specified};
+use layout::model::{Specified, Auto, specified};
use layout::wrapper::ThreadSafeLayoutNode;
-use std::cell::RefCell;
-use style::computed_values::table_layout;
-use geom::{Point2D, Rect, Size2D};
-use gfx::display_list::DisplayListCollection;
+use gfx::display_list::StackingContext;
use servo_util::geometry::Au;
use servo_util::geometry;
+use style::computed_values::table_layout;
pub enum TableLayout {
FixedLayout,
@@ -103,77 +100,22 @@ impl TableWrapperFlow {
/// Assign height for table-wrapper flow.
/// `Assign height` of table-wrapper flow follows a similar process to that of block flow.
- /// However, table-wrapper flow doesn't consider collapsing margins for flow's children
- /// and calculating padding/border.
///
/// inline(always) because this is only ever called by in-order or non-in-order top-level
/// methods
#[inline(always)]
- fn assign_height_table_wrapper_base(&mut self, ctx: &mut LayoutContext, inorder: bool) {
-
- // Note: Ignoring clearance for absolute flows as of now.
- let ignore_clear = self.is_absolutely_positioned();
- let (clearance, top_offset, bottom_offset, left_offset) = self.block_flow.initialize_offsets(ignore_clear);
-
- self.block_flow.handle_children_floats_if_necessary(ctx, inorder,
- left_offset, top_offset);
-
- // Table wrapper flow has margin but is not collapsed with kids(table caption and table).
- let (margin_top, margin_bottom, _, _) = self.block_flow.precompute_margin();
-
- let mut cur_y = top_offset;
-
- for kid in self.block_flow.base.child_iter() {
- let child_node = flow::mut_base(kid);
- child_node.position.origin.y = cur_y;
- cur_y = cur_y + child_node.position.size.height;
- }
-
- // top_offset: top margin-edge of the topmost child.
- // hence, height = content height
- let mut height = cur_y - top_offset;
-
- // For an absolutely positioned element, store the content height and stop the function.
- if self.block_flow.store_content_height_if_absolutely_positioned(height) {
- return;
- }
-
- for box_ in self.block_flow.box_.iter() {
- let style = box_.style();
-
- // At this point, `height` is the height of the containing block, so passing `height`
- // as the second argument here effectively makes percentages relative to the containing
- // block per CSS 2.1 § 10.5.
- height = match MaybeAuto::from_style(style.Box.get().height, height) {
- Auto => height,
- Specified(value) => geometry::max(value, height)
- };
- }
-
- self.block_flow.compute_height_position(&mut height,
- Au(0),
- margin_top,
- margin_bottom,
- clearance);
-
- self.block_flow.set_floats_out_if_inorder(inorder, height, cur_y,
- top_offset, bottom_offset, left_offset);
- self.block_flow.assign_height_absolute_flows(ctx);
+ fn assign_height_table_wrapper_base(&mut self,
+ layout_context: &mut LayoutContext,
+ inorder: bool) {
+ self.block_flow.assign_height_block_base(layout_context, inorder, MarginsMayNotCollapse);
}
- pub fn build_display_list_table_wrapper<E:ExtraDisplayListData>(
- &mut self,
- builder: &DisplayListBuilder,
- container_block_size: &Size2D<Au>,
- absolute_cb_abs_position: Point2D<Au>,
- dirty: &Rect<Au>,
- index: uint,
- lists: &RefCell<DisplayListCollection<E>>)
- -> uint {
+ pub fn build_display_list_table_wrapper(&mut self,
+ stacking_context: &mut StackingContext,
+ builder: &mut DisplayListBuilder,
+ info: &DisplayListBuildingInfo) {
debug!("build_display_list_table_wrapper: same process as block flow");
- self.block_flow.build_display_list_block(builder, container_block_size,
- absolute_cb_abs_position,
- dirty, index, lists)
+ self.block_flow.build_display_list_block(stacking_context, builder, info);
}
}
@@ -197,7 +139,7 @@ impl Flow for TableWrapperFlow {
any boxes it is responsible for flowing. */
fn bubble_widths(&mut self, ctx: &mut LayoutContext) {
- /* find max width from child block contexts */
+ // get column widths info from table flow
for kid in self.block_flow.base.child_iter() {
assert!(kid.is_table_caption() || kid.is_table());
@@ -227,8 +169,6 @@ impl Flow for TableWrapperFlow {
let mut left_content_edge = Au::new(0);
let mut content_width = containing_block_width;
- self.block_flow.set_containing_width_if_float(containing_block_width);
-
let width_computer = TableWrapper;
width_computer.compute_used_width_table_wrapper(self, ctx, containing_block_width);
@@ -243,7 +183,12 @@ impl Flow for TableWrapperFlow {
_ => {}
}
- self.block_flow.propagate_assigned_width_to_children(left_content_edge, content_width, None);
+ // In case of fixed layout, column widths are calculated in table flow.
+ let assigned_col_widths = match self.table_layout {
+ FixedLayout => None,
+ AutoLayout => Some(self.col_widths.clone())
+ };
+ self.block_flow.propagate_assigned_width_to_children(left_content_edge, content_width, assigned_col_widths);
}
/// This is called on kid flows by a parent.
@@ -270,26 +215,6 @@ impl Flow for TableWrapperFlow {
}
}
- // CSS Section 8.3.1 - Collapsing Margins
- // `self`: the Flow whose margins we want to collapse.
- // `collapsing`: value to be set by this function. This tells us how much
- // of the top margin has collapsed with a previous margin.
- // `collapsible`: Potential collapsible margin at the bottom of this flow's box.
- fn collapse_margins(&mut self,
- top_margin_collapsible: bool,
- first_in_flow: &mut bool,
- margin_top: &mut Au,
- top_offset: &mut Au,
- collapsing: &mut Au,
- collapsible: &mut Au) {
- self.block_flow.collapse_margins(top_margin_collapsible,
- first_in_flow,
- margin_top,
- top_offset,
- collapsing,
- collapsible);
- }
-
fn debug_str(&self) -> ~str {
let txt = if self.is_float() {
~"TableWrapperFlow(Float): "
@@ -327,10 +252,12 @@ impl TableWrapper {
let mut input = self.compute_width_constraint_inputs(&mut table_wrapper.block_flow,
parent_flow_width,
ctx);
- match table_wrapper.table_layout {
+ let computed_width = match table_wrapper.table_layout {
FixedLayout => {
let fixed_cells_width = table_wrapper.col_widths.iter().fold(Au(0),
|sum, width| sum.add(width));
+
+ let mut computed_width = input.computed_width.specified_or_zero();
for box_ in table_wrapper.block_flow.box_.iter() {
let style = box_.style();
@@ -344,15 +271,72 @@ impl TableWrapper {
let border_left = style.Border.get().border_left_width;
let border_right = style.Border.get().border_right_width;
let padding_and_borders = padding_left + padding_right + border_left + border_right;
- let mut computed_width = input.computed_width.specified_or_zero();
// Compare border-edge widths. Because fixed_cells_width indicates content-width,
// padding and border values are added to fixed_cells_width.
computed_width = geometry::max(fixed_cells_width + padding_and_borders, computed_width);
- input.computed_width = Specified(computed_width);
}
+ computed_width
},
- _ => {}
- }
+ AutoLayout => {
+ // Automatic table layout is calculated according to CSS 2.1 § 17.5.2.2.
+ let mut cap_min = Au(0);
+ let mut cols_min = Au(0);
+ let mut cols_max = Au(0);
+ let mut col_min_widths = &~[];
+ let mut col_pref_widths = &~[];
+ for kid in table_wrapper.block_flow.base.child_iter() {
+ if kid.is_table_caption() {
+ cap_min = kid.as_block().base.intrinsic_widths.minimum_width;
+ } else {
+ assert!(kid.is_table());
+ cols_min = kid.as_block().base.intrinsic_widths.minimum_width;
+ cols_max = kid.as_block().base.intrinsic_widths.preferred_width;
+ col_min_widths = kid.col_min_widths();
+ col_pref_widths = kid.col_pref_widths();
+ }
+ }
+ // 'extra_width': difference between the calculated table width and minimum width
+ // required by all columns. It will be distributed over the columns.
+ let (width, extra_width) = match input.computed_width {
+ Auto => {
+ if input.available_width > geometry::max(cols_max, cap_min) {
+ if cols_max > cap_min {
+ table_wrapper.col_widths = col_pref_widths.clone();
+ (cols_max, Au(0))
+ } else {
+ (cap_min, cap_min - cols_min)
+ }
+ } else {
+ let max = if cols_min >= input.available_width && cols_min >= cap_min {
+ table_wrapper.col_widths = col_min_widths.clone();
+ cols_min
+ } else {
+ geometry::max(input.available_width, cap_min)
+ };
+ (max, max - cols_min)
+ }
+ },
+ Specified(width) => {
+ let max = if cols_min >= width && cols_min >= cap_min {
+ table_wrapper.col_widths = col_min_widths.clone();
+ cols_min
+ } else {
+ geometry::max(width, cap_min)
+ };
+ (max, max - cols_min)
+ }
+ };
+ // The extra width is distributed over the columns
+ if extra_width > Au(0) {
+ let cell_len = table_wrapper.col_widths.len() as f64;
+ table_wrapper.col_widths = col_min_widths.map(|width| {
+ width + extra_width.scale_by(1.0 / cell_len)
+ });
+ }
+ width
+ }
+ };
+ input.computed_width = Specified(computed_width);
input
}
}
diff --git a/src/components/main/layout/util.rs b/src/components/main/layout/util.rs
index 60969ad19d7..e9a2c47a16d 100644
--- a/src/components/main/layout/util.rs
+++ b/src/components/main/layout/util.rs
@@ -7,6 +7,7 @@ use layout::construct::{ConstructionResult, NoConstructionResult};
use layout::parallel::DomParallelInfo;
use layout::wrapper::{LayoutNode, TLayoutNode, ThreadSafeLayoutNode};
+use gfx::display_list::OpaqueNode;
use script::dom::bindings::js::JS;
use script::dom::bindings::utils::Reflectable;
use script::dom::node::Node;
@@ -146,6 +147,10 @@ pub struct PrivateLayoutData {
/// `ConstructionItem`. See comments in `construct.rs` for more details.
flow_construction_result: ConstructionResult,
+ before_flow_construction_result: ConstructionResult,
+
+ after_flow_construction_result: ConstructionResult,
+
/// Information needed during parallel traversals.
parallel: DomParallelInfo,
}
@@ -159,6 +164,8 @@ impl PrivateLayoutData {
after_style: None,
restyle_damage: None,
flow_construction_result: NoConstructionResult,
+ before_flow_construction_result: NoConstructionResult,
+ after_flow_construction_result: NoConstructionResult,
parallel: DomParallelInfo::new(),
}
}
@@ -200,40 +207,45 @@ impl<'ln> LayoutDataAccess for LayoutNode<'ln> {
}
}
-/// An opaque handle to a node. The only safe operation that can be performed on this node is to
-/// compare it to another opaque handle or to another node.
-///
-/// Because the script task's GC does not trace layout, node data cannot be safely stored in layout
-/// data structures. Also, layout code tends to be faster when the DOM is not being accessed, for
-/// locality reasons. Using `OpaqueNode` enforces this invariant.
-#[deriving(Clone, Eq)]
-pub struct OpaqueNode(uintptr_t);
-
-impl OpaqueNode {
+pub trait OpaqueNodeMethods {
/// Converts a DOM node (layout view) to an `OpaqueNode`.
- pub fn from_layout_node(node: &LayoutNode) -> OpaqueNode {
+ fn from_layout_node(node: &LayoutNode) -> Self;
+
+ /// Converts a thread-safe DOM node (layout view) to an `OpaqueNode`.
+ fn from_thread_safe_layout_node(node: &ThreadSafeLayoutNode) -> Self;
+
+ /// Converts a DOM node (script view) to an `OpaqueNode`.
+ fn from_script_node(node: TrustedNodeAddress) -> Self;
+
+ /// Converts a DOM node to an `OpaqueNode'.
+ fn from_jsmanaged(node: &JS<Node>) -> Self;
+
+ /// Converts this node to an `UntrustedNodeAddress`. An `UntrustedNodeAddress` is just the type
+ /// of node that script expects to receive in a hit test.
+ fn to_untrusted_node_address(&self) -> UntrustedNodeAddress;
+}
+
+impl OpaqueNodeMethods for OpaqueNode {
+ fn from_layout_node(node: &LayoutNode) -> OpaqueNode {
unsafe {
- OpaqueNode::from_jsmanaged(node.get_jsmanaged())
+ OpaqueNodeMethods::from_jsmanaged(node.get_jsmanaged())
}
}
- /// Converts a thread-safe DOM node (layout view) to an `OpaqueNode`.
- pub fn from_thread_safe_layout_node(node: &ThreadSafeLayoutNode) -> OpaqueNode {
+ fn from_thread_safe_layout_node(node: &ThreadSafeLayoutNode) -> OpaqueNode {
unsafe {
let abstract_node = node.get_jsmanaged();
- let ptr: uintptr_t = cast::transmute(abstract_node.reflector().get_jsobject());
+ let ptr: uintptr_t = abstract_node.reflector().get_jsobject() as uint;
OpaqueNode(ptr)
}
}
- /// Converts a DOM node (script view) to an `OpaqueNode`.
- pub fn from_script_node(node: TrustedNodeAddress) -> OpaqueNode {
+ fn from_script_node(node: TrustedNodeAddress) -> OpaqueNode {
unsafe {
- OpaqueNode::from_jsmanaged(&JS::from_trusted_node_address(node))
+ OpaqueNodeMethods::from_jsmanaged(&JS::from_trusted_node_address(node))
}
}
- /// Converts a DOM node to an `OpaqueNode'.
fn from_jsmanaged(node: &JS<Node>) -> OpaqueNode {
unsafe {
let ptr: uintptr_t = cast::transmute(node.reflector().get_jsobject());
@@ -241,9 +253,7 @@ impl OpaqueNode {
}
}
- /// Converts this node to an `UntrustedNodeAddress`. An `UntrustedNodeAddress` is just the type
- /// of node that script expects to receive in a hit test.
- pub fn to_untrusted_node_address(&self) -> UntrustedNodeAddress {
+ fn to_untrusted_node_address(&self) -> UntrustedNodeAddress {
unsafe {
let OpaqueNode(addr) = *self;
let addr: UntrustedNodeAddress = cast::transmute(addr);
@@ -251,11 +261,5 @@ impl OpaqueNode {
}
}
- /// Returns the address of this node, for debugging purposes.
- pub fn id(&self) -> uintptr_t {
- unsafe {
- cast::transmute_copy(self)
- }
- }
}
diff --git a/src/components/main/layout/wrapper.rs b/src/components/main/layout/wrapper.rs
index ddd4a351b0b..755b5aa9fa7 100644
--- a/src/components/main/layout/wrapper.rs
+++ b/src/components/main/layout/wrapper.rs
@@ -50,7 +50,7 @@ use std::cast;
use std::cell::{Ref, RefMut};
use style::{PropertyDeclarationBlock, TElement, TNode, AttrSelector, SpecificNamespace};
use style::{AnyNamespace};
-
+use style::computed_values::{content, display};
use layout::util::LayoutDataWrapper;
/// Allows some convenience methods on generic layout nodes.
@@ -58,8 +58,9 @@ pub trait TLayoutNode {
/// Creates a new layout node with the same lifetime as this layout node.
unsafe fn new_with_this_lifetime(&self, node: &JS<Node>) -> Self;
- /// Returns the type ID of this node. Fails if this node is borrowed mutably.
- fn type_id(&self) -> NodeTypeId;
+ /// Returns the type ID of this node. Fails if this node is borrowed mutably. Returns `None`
+ /// if this is a pseudo-element; otherwise, returns `Some`.
+ fn type_id(&self) -> Option<NodeTypeId>;
/// Returns the interior of this node as a `JS`. This is highly unsafe for layout to
/// call and as such is marked `unsafe`.
@@ -73,14 +74,14 @@ pub trait TLayoutNode {
fn node_is_element(&self) -> bool {
match self.type_id() {
- ElementNodeTypeId(..) => true,
+ Some(ElementNodeTypeId(..)) => true,
_ => false
}
}
fn node_is_document(&self) -> bool {
match self.type_id() {
- DocumentNodeTypeId(..) => true,
+ Some(DocumentNodeTypeId(..)) => true,
_ => false
}
}
@@ -114,22 +115,10 @@ pub trait TLayoutNode {
/// If this is a text node, copies out the text. If this is not a text node, fails.
///
/// FIXME(pcwalton): Don't copy text. Atomically reference count instead.
- fn text(&self) -> ~str {
- unsafe {
- if !self.get().is_text() {
- fail!("not text!")
- }
- let text: JS<Text> = self.get_jsmanaged().transmute_copy();
- (*text.unsafe_get()).characterdata.data.to_str()
- }
- }
+ fn text(&self) -> ~str;
/// Returns the first child of this node.
- fn first_child(&self) -> Option<Self> {
- unsafe {
- self.get().first_child_ref().map(|node| self.new_with_this_lifetime(node))
- }
- }
+ fn first_child(&self) -> Option<Self>;
/// Dumps this node tree, for debugging.
fn dump(&self) {
@@ -144,7 +133,7 @@ pub struct LayoutNode<'a> {
priv node: JS<Node>,
/// Being chained to a value prevents `LayoutNode`s from escaping.
- priv chain: &'a (),
+ chain: &'a (),
}
impl<'ln> Clone for LayoutNode<'ln> {
@@ -172,12 +161,30 @@ impl<'ln> TLayoutNode for LayoutNode<'ln> {
chain: self.chain,
}
}
- fn type_id(&self) -> NodeTypeId {
- self.node.type_id()
+
+ fn type_id(&self) -> Option<NodeTypeId> {
+ Some(self.node.type_id())
}
+
unsafe fn get_jsmanaged<'a>(&'a self) -> &'a JS<Node> {
&self.node
}
+
+ fn first_child(&self) -> Option<LayoutNode<'ln>> {
+ unsafe {
+ self.get().first_child_ref().map(|node| self.new_with_this_lifetime(node))
+ }
+ }
+
+ fn text(&self) -> ~str {
+ unsafe {
+ if !self.get().is_text() {
+ fail!("not text!")
+ }
+ let text: JS<Text> = self.get_jsmanaged().transmute_copy();
+ (*text.unsafe_get()).characterdata.data.to_str()
+ }
+ }
}
impl<'ln> LayoutNode<'ln> {
@@ -205,6 +212,10 @@ impl<'ln> LayoutNode<'ln> {
current_node: self.first_child(),
}
}
+
+ pub unsafe fn get_jsmanaged<'a>(&'a self) -> &'a JS<Node> {
+ &self.node
+ }
}
impl<'ln> TNode<LayoutElement<'ln>> for LayoutNode<'ln> {
@@ -371,29 +382,107 @@ impl<'le> TElement for LayoutElement<'le> {
}
}
+fn get_content(content_list: &content::T) -> ~str {
+ match *content_list {
+ content::Content(ref value) => {
+ let iter = &mut value.clone().move_iter().peekable();
+ match iter.next() {
+ Some(content::StringContent(content)) => content,
+ _ => ~"",
+ }
+ }
+ _ => ~"",
+ }
+}
+
+#[deriving(Eq, Clone)]
+pub enum ElementType {
+ Normal,
+ Before,
+ After,
+ BeforeBlock,
+ AfterBlock,
+}
+
/// A thread-safe version of `LayoutNode`, used during flow construction. This type of layout
/// node does not allow any parents or siblings of nodes to be accessed, to avoid races.
pub struct ThreadSafeLayoutNode<'ln> {
/// The wrapped node.
- priv node: JS<Node>,
+ priv node: LayoutNode<'ln>,
- /// Being chained to a value prevents `ThreadSafeLayoutNode`s from escaping.
- priv chain: &'ln (),
+ priv pseudo: ElementType,
}
impl<'ln> TLayoutNode for ThreadSafeLayoutNode<'ln> {
/// Creates a new layout node with the same lifetime as this layout node.
unsafe fn new_with_this_lifetime(&self, node: &JS<Node>) -> ThreadSafeLayoutNode<'ln> {
ThreadSafeLayoutNode {
- node: node.transmute_copy(),
- chain: self.chain,
+ node: LayoutNode {
+ node: node.transmute_copy(),
+ chain: self.node.chain,
+ },
+ pseudo: Normal,
}
}
- fn type_id(&self) -> NodeTypeId {
+
+ /// Returns `None` if this is a pseudo-element.
+ fn type_id(&self) -> Option<NodeTypeId> {
+ if self.pseudo == Before || self.pseudo == After {
+ return None
+ }
+
self.node.type_id()
}
+
unsafe fn get_jsmanaged<'a>(&'a self) -> &'a JS<Node> {
- &self.node
+ self.node.get_jsmanaged()
+ }
+
+ unsafe fn get<'a>(&'a self) -> &'a Node { // this change.
+ cast::transmute::<*mut Node,&'a Node>(self.get_jsmanaged().unsafe_get())
+ }
+
+ fn first_child(&self) -> Option<ThreadSafeLayoutNode<'ln>> {
+ if self.pseudo == Before || self.pseudo == After {
+ return None
+ }
+
+ if self.has_before_pseudo() {
+ if self.is_block(Before) && self.pseudo == Normal {
+ let pseudo_before_node = ThreadSafeLayoutNode::new_with_pseudo_without_self(&self.node, BeforeBlock);
+ return Some(pseudo_before_node)
+ } else if self.pseudo == Normal || self.pseudo == BeforeBlock {
+ let pseudo_before_node = ThreadSafeLayoutNode::new_with_pseudo_without_self(&self.node, Before);
+ return Some(pseudo_before_node)
+ }
+ }
+
+ unsafe {
+ self.get().first_child_ref().map(|node| self.new_with_this_lifetime(node))
+ }
+ }
+
+ fn text(&self) -> ~str {
+ if self.pseudo == Before || self.pseudo == After {
+ let layout_data_ref = self.borrow_layout_data();
+ let node_layout_data_wrapper = layout_data_ref.get().get_ref();
+
+ if self.pseudo == Before {
+ let before_style = node_layout_data_wrapper.data.before_style.get_ref();
+ return get_content(&before_style.get().Box.get().content)
+ } else {
+ let after_style = node_layout_data_wrapper.data.after_style.get_ref();
+ return get_content(&after_style.get().Box.get().content)
+ }
+ }
+
+ unsafe {
+ if !self.get().is_text() {
+ fail!("not text!")
+ }
+ let text: JS<Text> = self.get_jsmanaged().transmute_copy();
+ (*text.unsafe_get()).characterdata.data.to_str()
+ }
}
}
@@ -401,7 +490,7 @@ impl<'ln> Clone for ThreadSafeLayoutNode<'ln> {
fn clone(&self) -> ThreadSafeLayoutNode<'ln> {
ThreadSafeLayoutNode {
node: self.node.clone(),
- chain: self.chain,
+ pseudo: self.pseudo,
}
}
}
@@ -410,13 +499,33 @@ impl<'ln> ThreadSafeLayoutNode<'ln> {
/// Creates a new `ThreadSafeLayoutNode` from the given `LayoutNode`.
pub fn new<'a>(node: &LayoutNode<'a>) -> ThreadSafeLayoutNode<'a> {
ThreadSafeLayoutNode {
- node: node.node.clone(),
- chain: node.chain,
+ node: node.clone(),
+ pseudo: Normal,
+ }
+ }
+
+ pub fn new_with_pseudo_without_self<'a>(node: &LayoutNode<'a>, element_type: ElementType) -> ThreadSafeLayoutNode<'a> {
+ ThreadSafeLayoutNode {
+ node: node.clone(),
+ pseudo: element_type,
+ }
+ }
+
+
+ /// Creates a new `ThreadSafeLayoutNode` from the given `LayoutNode`.
+ pub fn new_with_pseudo<'a>(&'a self, element_type: ElementType) -> ThreadSafeLayoutNode<'a> {
+ ThreadSafeLayoutNode {
+ node: self.node.clone(),
+ pseudo: element_type,
}
}
/// Returns the next sibling of this node. Unsafe and private because this can lead to races.
unsafe fn next_sibling(&self) -> Option<ThreadSafeLayoutNode<'ln>> {
+ if self.pseudo == Before || self.pseudo == BeforeBlock {
+ return self.get().first_child_ref().map(|node| self.new_with_this_lifetime(node))
+ }
+
self.node.get().next_sibling_ref().map(|node| self.new_with_this_lifetime(node))
}
@@ -424,6 +533,7 @@ impl<'ln> ThreadSafeLayoutNode<'ln> {
pub fn children(&self) -> ThreadSafeLayoutNodeChildrenIterator<'ln> {
ThreadSafeLayoutNodeChildrenIterator {
current_node: self.first_child(),
+ parent_node: Some(ThreadSafeLayoutNode::new_with_pseudo_without_self(&self.node, self.pseudo)),
}
}
@@ -431,7 +541,7 @@ impl<'ln> ThreadSafeLayoutNode<'ln> {
#[inline]
pub fn as_element(&self) -> ThreadSafeLayoutElement {
unsafe {
- let elem: JS<Element> = self.node.transmute_copy();
+ let elem: JS<Element> = self.node.get_jsmanaged().transmute_copy();
let element = elem.unsafe_get();
// FIXME(pcwalton): Workaround until Rust gets multiple lifetime parameters on
// implementations.
@@ -441,6 +551,44 @@ impl<'ln> ThreadSafeLayoutNode<'ln> {
}
}
+ pub fn get_element_type(&self) -> ElementType {
+ self.pseudo
+ }
+
+ pub fn is_block(&self, kind: ElementType) -> bool {
+ let mut layout_data_ref = self.mutate_layout_data();
+ let node_layout_data_wrapper = layout_data_ref.get().get_mut_ref();
+
+ let display = match kind {
+ Before | BeforeBlock => {
+ let before_style = node_layout_data_wrapper.data.before_style.get_ref();
+ before_style.get().Box.get().display
+ }
+ After | AfterBlock => {
+ let after_style = node_layout_data_wrapper.data.after_style.get_ref();
+ after_style.get().Box.get().display
+ }
+ Normal => {
+ let after_style = node_layout_data_wrapper.data.style.get_ref();
+ after_style.get().Box.get().display
+ }
+ };
+
+ display == display::block
+ }
+
+ pub fn has_before_pseudo(&self) -> bool {
+ let layout_data_wrapper = self.borrow_layout_data();
+ let layout_data_wrapper_ref = layout_data_wrapper.get().get_ref();
+ layout_data_wrapper_ref.data.before_style.is_some()
+ }
+
+ pub fn has_after_pseudo(&self) -> bool {
+ let layout_data_wrapper = self.borrow_layout_data();
+ let layout_data_wrapper_ref = layout_data_wrapper.get().get_ref();
+ layout_data_wrapper_ref.data.after_style.is_some()
+ }
+
/// Borrows the layout data immutably. Fails on a conflicting borrow.
#[inline(always)]
pub fn borrow_layout_data<'a>(&'a self) -> Ref<'a,Option<LayoutDataWrapper>> {
@@ -487,16 +635,56 @@ impl<'ln> ThreadSafeLayoutNode<'ln> {
pub struct ThreadSafeLayoutNodeChildrenIterator<'a> {
priv current_node: Option<ThreadSafeLayoutNode<'a>>,
+ priv parent_node: Option<ThreadSafeLayoutNode<'a>>,
}
impl<'a> Iterator<ThreadSafeLayoutNode<'a>> for ThreadSafeLayoutNodeChildrenIterator<'a> {
fn next(&mut self) -> Option<ThreadSafeLayoutNode<'a>> {
let node = self.current_node.clone();
- self.current_node = self.current_node.clone().and_then(|node| {
- unsafe {
- node.next_sibling()
+
+ match node {
+ Some(ref node) => {
+ if node.pseudo == After || node.pseudo == AfterBlock {
+ return None
+ }
+
+ match self.parent_node {
+ Some(ref parent_node) => {
+ if parent_node.pseudo == Normal {
+ self.current_node = self.current_node.clone().and_then(|node| {
+ unsafe {
+ node.next_sibling()
+ }
+ });
+ } else {
+ self.current_node = None;
+ }
+ }
+ None => {}
+ }
}
- });
+ None => {
+ match self.parent_node {
+ Some(ref parent_node) => {
+ if parent_node.has_after_pseudo() {
+ let pseudo_after_node = if parent_node.is_block(After) && parent_node.pseudo == Normal {
+ let pseudo_after_node = ThreadSafeLayoutNode::new_with_pseudo_without_self(&parent_node.node, AfterBlock);
+ Some(pseudo_after_node)
+ } else if parent_node.pseudo == Normal || parent_node.pseudo == AfterBlock {
+ let pseudo_after_node = ThreadSafeLayoutNode::new_with_pseudo_without_self(&parent_node.node, After);
+ Some(pseudo_after_node)
+ } else {
+ None
+ };
+ self.current_node = pseudo_after_node;
+ return self.current_node.clone()
+ }
+ }
+ None => {}
+ }
+ }
+ }
+
node
}
}
diff --git a/src/components/main/pipeline.rs b/src/components/main/pipeline.rs
index 77782342c12..2cca7feeb97 100644
--- a/src/components/main/pipeline.rs
+++ b/src/components/main/pipeline.rs
@@ -9,7 +9,6 @@ use extra::url::Url;
use geom::size::Size2D;
use gfx::render_task::{PaintPermissionGranted, PaintPermissionRevoked};
use gfx::render_task::{RenderChan, RenderTask};
-use layout::util::OpaqueNode;
use script::layout_interface::LayoutChan;
use script::script_task::LoadMsg;
use script::script_task::{AttachLayoutMsg, NewLayoutInfo, ScriptTask, ScriptChan};
@@ -28,7 +27,7 @@ pub struct Pipeline {
subpage_id: Option<SubpageId>,
script_chan: ScriptChan,
layout_chan: LayoutChan,
- render_chan: RenderChan<OpaqueNode>,
+ render_chan: RenderChan,
layout_shutdown_port: Port<()>,
render_shutdown_port: Port<()>,
/// The most recently loaded url
@@ -40,7 +39,7 @@ pub struct Pipeline {
pub struct CompositionPipeline {
id: PipelineId,
script_chan: ScriptChan,
- render_chan: RenderChan<OpaqueNode>,
+ render_chan: RenderChan,
}
impl Pipeline {
@@ -171,7 +170,7 @@ impl Pipeline {
subpage_id: Option<SubpageId>,
script_chan: ScriptChan,
layout_chan: LayoutChan,
- render_chan: RenderChan<OpaqueNode>,
+ render_chan: RenderChan,
layout_shutdown_port: Port<()>,
render_shutdown_port: Port<()>)
-> Pipeline {
diff --git a/src/components/msg/compositor_msg.rs b/src/components/msg/compositor_msg.rs
index 847a13d1ab7..8518bd3024a 100644
--- a/src/components/msg/compositor_msg.rs
+++ b/src/components/msg/compositor_msg.rs
@@ -2,17 +2,18 @@
* 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/. */
+use azure::azure_hl::Color;
use geom::point::Point2D;
use geom::rect::Rect;
use geom::size::Size2D;
-use azure::azure_hl::Color;
use layers::platform::surface::{NativeGraphicsMetadata, NativePaintingGraphicsContext};
use layers::platform::surface::{NativeSurface, NativeSurfaceMethods};
+use serialize::{Encoder, Encodable};
+use std::fmt::{Formatter, Show};
+use std::fmt;
use constellation_msg::PipelineId;
-use serialize::{Encoder, Encodable};
-
pub struct LayerBuffer {
/// The native surface which can be shared between threads or processes. On Mac this is an
/// `IOSurface`; on Linux this is an X Pixmap; on Android this is an `EGLImageKHR`.
@@ -76,15 +77,73 @@ impl Epoch {
}
}
+#[deriving(Clone, Eq)]
+pub struct LayerId(uint, uint);
+
+impl Show for LayerId {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ let LayerId(a, b) = *self;
+ write!(f.buf, "Layer({}, {})", a, b);
+ Ok(())
+ }
+}
+
+impl LayerId {
+ /// FIXME(#2011, pcwalton): This is unfortunate. Maybe remove this in the future.
+ pub fn null() -> LayerId {
+ LayerId(0, 0)
+ }
+}
+
+/// The scrolling policy of a layer.
+#[deriving(Eq)]
+pub enum ScrollPolicy {
+ /// These layers scroll when the parent receives a scrolling message.
+ Scrollable,
+ /// These layers do not scroll when the parent receives a scrolling message.
+ FixedPosition,
+}
+
+/// All layer-specific information that the painting task sends to the compositor other than the
+/// buffer contents of the layer itself.
+pub struct LayerMetadata {
+ /// An opaque ID. This is usually the address of the flow and index of the box within it.
+ id: LayerId,
+ /// The position and size of the layer in pixels.
+ position: Rect<uint>,
+ /// The background color of the layer.
+ background_color: Color,
+ /// The scrolling policy of this layer.
+ scroll_policy: ScrollPolicy,
+}
+
/// The interface used by the renderer to acquire draw targets for each render frame and
/// submit them to be drawn to the display.
pub trait RenderListener {
fn get_graphics_metadata(&self) -> Option<NativeGraphicsMetadata>;
- fn new_layer(&self, PipelineId, Size2D<uint>);
- fn set_layer_page_size_and_color(&self, PipelineId, Size2D<uint>, Epoch, Color);
- fn set_layer_clip_rect(&self, PipelineId, Rect<uint>);
- fn delete_layer(&self, PipelineId);
- fn paint(&self, id: PipelineId, layer_buffer_set: ~LayerBufferSet, Epoch);
+ fn create_layer_group_for_pipeline(&self, PipelineId, Size2D<uint>);
+
+ /// Informs the compositor of the layers for the given pipeline. The compositor responds by
+ /// creating and/or destroying render layers as necessary.
+ fn initialize_layers_for_pipeline(&self,
+ pipeline_id: PipelineId,
+ metadata: ~[LayerMetadata],
+ epoch: Epoch);
+
+ fn set_layer_clip_rect(&self,
+ pipeline_id: PipelineId,
+ layer_id: LayerId,
+ new_rect: Rect<uint>);
+
+ fn delete_layer_group(&self, PipelineId);
+
+ /// Sends new tiles for the given layer to the compositor.
+ fn paint(&self,
+ pipeline_id: PipelineId,
+ layer_id: LayerId,
+ layer_buffer_set: ~LayerBufferSet,
+ epoch: Epoch);
+
fn set_render_state(&self, render_state: RenderState);
}
@@ -92,8 +151,11 @@ pub trait RenderListener {
/// which is used in displaying the appropriate message in the window's title.
pub trait ScriptListener : Clone {
fn set_ready_state(&self, ReadyState);
- fn invalidate_rect(&self, PipelineId, Rect<uint>);
- fn scroll_fragment_point(&self, PipelineId, Point2D<f32>);
+ fn invalidate_rect(&self, pipeline_id: PipelineId, layer_id: LayerId, rect: Rect<uint>);
+ fn scroll_fragment_point(&self,
+ pipeline_id: PipelineId,
+ layer_id: LayerId,
+ point: Point2D<f32>);
fn close(&self);
fn dup(&self) -> ~ScriptListener;
}
diff --git a/src/components/script/script_task.rs b/src/components/script/script_task.rs
index 2fba7693687..9d9897dfc54 100644
--- a/src/components/script/script_task.rs
+++ b/src/components/script/script_task.rs
@@ -37,7 +37,8 @@ use js::jsapi::{JSObject, JS_InhibitGC, JS_AllowGC, JS_CallFunctionValue};
use js::jsval::NullValue;
use js::rust::{Compartment, Cx, CxUtils, RtUtils};
use js;
-use servo_msg::compositor_msg::{FinishedLoading, Loading, PerformingLayout, ScriptListener};
+use servo_msg::compositor_msg::{FinishedLoading, LayerId, Loading, PerformingLayout};
+use servo_msg::compositor_msg::{ScriptListener};
use servo_msg::constellation_msg::{ConstellationChan, IFrameSandboxed, IFrameUnsandboxed};
use servo_msg::constellation_msg::{LoadIframeUrlMsg, LoadCompleteMsg, LoadUrlMsg, NavigationDirection};
use servo_msg::constellation_msg::{PipelineId, SubpageId, Failure, FailureMsg};
@@ -929,10 +930,15 @@ impl ScriptTask {
fn scroll_fragment_point(&self, pipeline_id: PipelineId, page: &Page, node: JS<Element>) {
let (port, chan) = Chan::new();
let node: JS<Node> = NodeCast::from(&node);
- let ContentBoxResponse(rect) = page.query_layout(ContentBoxQuery(node.to_trusted_node_address(), chan), port);
+ let ContentBoxResponse(rect) =
+ page.query_layout(ContentBoxQuery(node.to_trusted_node_address(), chan), port);
let point = Point2D(to_frac_px(rect.origin.x).to_f32().unwrap(),
to_frac_px(rect.origin.y).to_f32().unwrap());
- self.compositor.scroll_fragment_point(pipeline_id, point);
+ // FIXME(#2003, pcwalton): This is pretty bogus when multiple layers are involved.
+ // Really what needs to happen is that this needs to go through layout to ask which
+ // layer the element belongs to, and have it send the scroll message to the
+ // compositor.
+ self.compositor.scroll_fragment_point(pipeline_id, LayerId::null(), point);
}
/// This is the main entry point for receiving and dispatching DOM events.
diff --git a/src/components/style/common_types.rs b/src/components/style/common_types.rs
index 9650f9e83f1..655646790bb 100644
--- a/src/components/style/common_types.rs
+++ b/src/components/style/common_types.rs
@@ -8,6 +8,7 @@ pub use servo_util::geometry::Au;
pub type CSSFloat = f64;
+pub static DEFAULT_LINE_HEIGHT: CSSFloat = 1.14;
pub mod specified {
use std::ascii::StrAsciiExt;
@@ -172,7 +173,10 @@ pub mod computed {
color: longhands::color::computed_value::T,
inherited_font_weight: longhands::font_weight::computed_value::T,
inherited_font_size: longhands::font_size::computed_value::T,
+ inherited_minimum_line_height: longhands::_servo_minimum_line_height::T,
+ inherited_height: longhands::height::T,
font_size: longhands::font_size::computed_value::T,
+ display: longhands::display::computed_value::T,
positioned: bool,
floated: bool,
border_top_present: bool,
diff --git a/src/components/style/properties.rs.mako b/src/components/style/properties.rs.mako
index db8cf392722..118eee0f010 100644
--- a/src/components/style/properties.rs.mako
+++ b/src/components/style/properties.rs.mako
@@ -32,10 +32,11 @@ def to_rust_ident(name):
return name
class Longhand(object):
- def __init__(self, name):
+ def __init__(self, name, derived_from=None):
self.name = name
self.ident = to_rust_ident(name)
self.style_struct = THIS_STYLE_STRUCT
+ self.derived_from = None if derived_from is None else to_rust_ident(derived_from)
class Shorthand(object):
def __init__(self, name, sub_properties):
@@ -53,6 +54,7 @@ STYLE_STRUCTS = []
THIS_STYLE_STRUCT = None
LONGHANDS = []
LONGHANDS_BY_NAME = {}
+DERIVED_LONGHANDS = {}
SHORTHANDS = []
def new_style_struct(name, is_inherited):
@@ -81,12 +83,13 @@ pub mod longhands {
value
}
- <%def name="raw_longhand(name, no_super=False)">
+ <%def name="raw_longhand(name, no_super=False, derived_from=None)">
<%
- property = Longhand(name)
+ property = Longhand(name, derived_from=derived_from)
THIS_STYLE_STRUCT.longhands.append(property)
LONGHANDS.append(property)
LONGHANDS_BY_NAME[name] = property
+ DERIVED_LONGHANDS.setdefault(derived_from, []).append(property)
%>
pub mod ${property.ident} {
% if not no_super:
@@ -94,30 +97,34 @@ pub mod longhands {
% endif
pub use self::computed_value::*;
${caller.body()}
- pub fn parse_declared(input: &[ComponentValue], base_url: &Url)
- -> Option<DeclaredValue<SpecifiedValue>> {
- match CSSWideKeyword::parse(input) {
- Some(Some(keyword)) => Some(CSSWideKeyword(keyword)),
- Some(None) => Some(CSSWideKeyword(${
- "Inherit" if THIS_STYLE_STRUCT.inherited else "Initial"})),
- None => parse_specified(input, base_url),
+ % if derived_from is None:
+ pub fn parse_declared(input: &[ComponentValue], base_url: &Url)
+ -> Option<DeclaredValue<SpecifiedValue>> {
+ match CSSWideKeyword::parse(input) {
+ Some(Some(keyword)) => Some(CSSWideKeyword(keyword)),
+ Some(None) => Some(CSSWideKeyword(${
+ "Inherit" if THIS_STYLE_STRUCT.inherited else "Initial"})),
+ None => parse_specified(input, base_url),
+ }
}
- }
+ % endif
}
</%def>
- <%def name="longhand(name, no_super=False)">
- <%self:raw_longhand name="${name}">
+ <%def name="longhand(name, no_super=False, derived_from=None)">
+ <%self:raw_longhand name="${name}" derived_from="${derived_from}">
${caller.body()}
- pub fn parse_specified(input: &[ComponentValue], base_url: &Url)
- -> Option<DeclaredValue<SpecifiedValue>> {
- parse(input, base_url).map(super::SpecifiedValue)
- }
+ % if derived_from is None:
+ pub fn parse_specified(_input: &[ComponentValue], _base_url: &Url)
+ -> Option<DeclaredValue<SpecifiedValue>> {
+ parse(_input, _base_url).map(super::SpecifiedValue)
+ }
+ % endif
</%self:raw_longhand>
</%def>
- <%def name="single_component_value(name)">
- <%self:longhand name="${name}">
+ <%def name="single_component_value(name, derived_from=None)">
+ <%self:longhand name="${name}" derived_from="${derived_from}">
${caller.body()}
pub fn parse(input: &[ComponentValue], base_url: &Url) -> Option<SpecifiedValue> {
one_component_value(input).and_then(|c| from_component_value(c, base_url))
@@ -307,9 +314,29 @@ pub mod longhands {
${predefined_type("width", "LengthOrPercentageOrAuto",
"computed::LPA_Auto",
"parse_non_negative")}
- ${predefined_type("height", "LengthOrPercentageOrAuto",
- "computed::LPA_Auto",
- "parse_non_negative")}
+ <%self:single_component_value name="height">
+ pub type SpecifiedValue = specified::LengthOrPercentageOrAuto;
+ pub mod computed_value {
+ pub type T = super::super::computed::LengthOrPercentageOrAuto;
+ }
+ #[inline]
+ pub fn get_initial_value() -> computed_value::T { computed::LPA_Auto }
+ #[inline]
+ pub fn from_component_value(v: &ComponentValue, _base_url: &Url)
+ -> Option<SpecifiedValue> {
+ specified::LengthOrPercentageOrAuto::parse_non_negative(v)
+ }
+ pub fn to_computed_value(value: SpecifiedValue, context: &computed::Context)
+ -> computed_value::T {
+ match (value, context.inherited_height) {
+ (specified::LPA_Percentage(_), computed::LPA_Auto)
+ if !context.is_root_element && !context.positioned => {
+ computed::LPA_Auto
+ },
+ _ => computed::compute_LengthOrPercentageOrAuto(value, context)
+ }
+ }
+ </%self:single_component_value>
${predefined_type("min-width", "LengthOrPercentage",
"computed::LP_Length(Au(0))",
@@ -318,6 +345,13 @@ pub mod longhands {
"computed::LPN_None",
"parse_non_negative")}
+ ${predefined_type("min-height", "LengthOrPercentage",
+ "computed::LP_Length(Au(0))",
+ "parse_non_negative")}
+ ${predefined_type("max-height", "LengthOrPercentageOrNone",
+ "computed::LPN_None",
+ "parse_non_negative")}
+
${new_style_struct("InheritedBox", is_inherited=True)}
<%self:single_component_value name="line-height">
@@ -366,6 +400,40 @@ pub mod longhands {
}
</%self:single_component_value>
+ <%self:longhand name="-servo-minimum-line-height" derived_from="line-height">
+ use super::Au;
+ use super::super::common_types::DEFAULT_LINE_HEIGHT;
+ use super::super::longhands::display;
+ use super::super::longhands::line_height;
+
+ pub use to_computed_value = super::computed_as_specified;
+
+ pub type SpecifiedValue = line_height::SpecifiedValue;
+
+ pub mod computed_value {
+ pub type T = super::super::Au;
+ }
+
+ #[inline]
+ pub fn get_initial_value() -> computed_value::T {
+ Au(0)
+ }
+
+ #[inline]
+ pub fn derive(value: line_height::computed_value::T, context: &computed::Context)
+ -> Au {
+ if context.display != display::computed_value::inline {
+ match value {
+ line_height::Normal => context.font_size.scale_by(DEFAULT_LINE_HEIGHT),
+ line_height::Number(percentage) => context.font_size.scale_by(percentage),
+ line_height::Length(length) => length,
+ }
+ } else {
+ context.inherited_minimum_line_height
+ }
+ }
+ </%self:longhand>
+
${switch_to_style_struct("Box")}
<%self:single_component_value name="vertical-align">
@@ -519,6 +587,81 @@ pub mod longhands {
}
</%self:single_component_value>
+ <%self:longhand name="background-position">
+ use super::super::common_types::specified;
+
+ pub mod computed_value {
+ use super::super::super::common_types::computed::LengthOrPercentage;
+
+ #[deriving(Eq, Clone)]
+ pub struct T {
+ horizontal: LengthOrPercentage,
+ vertical: LengthOrPercentage,
+ }
+ }
+
+ #[deriving(Clone)]
+ pub struct SpecifiedValue {
+ horizontal: specified::LengthOrPercentage,
+ vertical: specified::LengthOrPercentage,
+ }
+
+ #[inline]
+ pub fn to_computed_value(value: SpecifiedValue, context: &computed::Context)
+ -> computed_value::T {
+ computed_value::T {
+ horizontal: computed::compute_LengthOrPercentage(value.horizontal, context),
+ vertical: computed::compute_LengthOrPercentage(value.vertical, context),
+ }
+ }
+
+ #[inline]
+ pub fn get_initial_value() -> computed_value::T {
+ computed_value::T {
+ horizontal: computed::LP_Percentage(0.0),
+ vertical: computed::LP_Percentage(0.0),
+ }
+ }
+
+ // FIXME(#1997, pcwalton): Support complete CSS2 syntax.
+ pub fn parse_horizontal_and_vertical(horiz: &ComponentValue, vert: &ComponentValue)
+ -> Option<SpecifiedValue> {
+ let horiz = match specified::LengthOrPercentage::parse_non_negative(horiz) {
+ None => return None,
+ Some(value) => value,
+ };
+
+ let vert = match specified::LengthOrPercentage::parse_non_negative(vert) {
+ None => return None,
+ Some(value) => value,
+ };
+
+ Some(SpecifiedValue {
+ horizontal: horiz,
+ vertical: vert,
+ })
+ }
+
+ pub fn parse(input: &[ComponentValue], _: &Url) -> Option<SpecifiedValue> {
+ let mut input_iter = input.skip_whitespace();
+ let horizontal = input_iter.next();
+ let vertical = input_iter.next();
+ if input_iter.next().is_some() {
+ return None
+ }
+
+ match (horizontal, vertical) {
+ (Some(horizontal), Some(vertical)) => {
+ parse_horizontal_and_vertical(horizontal, vertical)
+ }
+ _ => None
+ }
+ }
+ </%self:longhand>
+
+ ${single_keyword("background-repeat", "repeat repeat-x repeat-y no-repeat")}
+
+ ${single_keyword("background-attachment", "scroll fixed")}
${new_style_struct("Color", is_inherited=True)}
@@ -866,29 +1009,97 @@ pub mod shorthands {
</%def>
// TODO: other background-* properties
- <%self:shorthand name="background" sub_properties="background-color background-image">
- let mut color = None;
- let mut image = None;
+ <%self:shorthand name="background"
+ sub_properties="background-color background-position background-repeat background-attachment background-image">
+ use std::mem;
+
+ let (mut color, mut image, mut position, mut repeat, mut attachment) =
+ (None, None, None, None, None);
+ let mut last_component_value = None;
let mut any = false;
for component_value in input.skip_whitespace() {
if color.is_none() {
match background_color::from_component_value(component_value, base_url) {
- Some(v) => { color = Some(v); any = true; continue },
+ Some(v) => {
+ color = Some(v);
+ any = true;
+ continue
+ },
None => ()
}
}
if image.is_none() {
match background_image::from_component_value(component_value, base_url) {
- Some(v) => { image = Some(v); any = true; continue },
+ Some(v) => {
+ image = Some(v);
+ any = true;
+ continue
+ },
None => (),
}
}
- return None;
+
+ if repeat.is_none() {
+ match background_repeat::from_component_value(component_value, base_url) {
+ Some(v) => {
+ repeat = Some(v);
+ any = true;
+ continue
+ },
+ None => ()
+ }
+ }
+
+ if attachment.is_none() {
+ match background_attachment::from_component_value(component_value,
+ base_url) {
+ Some(v) => {
+ attachment = Some(v);
+ any = true;
+ continue
+ },
+ None => ()
+ }
+ }
+
+ match mem::replace(&mut last_component_value, None) {
+ Some(saved_component_value) => {
+ if position.is_none() {
+ match background_position::parse_horizontal_and_vertical(
+ saved_component_value,
+ component_value) {
+ Some(v) => {
+ position = Some(v);
+ any = true;
+ continue
+ },
+ None => (),
+ }
+ }
+
+ // If we get here, parsing failed.
+ return None
+ }
+ None => {
+ // Save the component value.
+ last_component_value = Some(component_value)
+ }
+ }
+ }
+
+ if any && last_component_value.is_none() {
+ Some(Longhands {
+ background_color: color,
+ background_image: image,
+ background_position: position,
+ background_repeat: repeat,
+ background_attachment: attachment,
+ })
+ } else {
+ None
}
- if any { Some(Longhands { background_color: color, background_image: image }) }
- else { None }
</%self:shorthand>
${four_sides_shorthand("margin", "margin-%s", "margin_top::from_component_value")}
@@ -1137,12 +1348,16 @@ impl PropertyDeclaration {
let name_lower = tmp_for_lifetime.as_str_ascii();
match name_lower.as_slice() {
% for property in LONGHANDS:
- "${property.name}" => result_list.push(${property.ident}_declaration(
- match longhands::${property.ident}::parse_declared(value, base_url) {
- Some(value) => value,
- None => return InvalidValue,
- }
- )),
+ % if property.derived_from is None:
+ "${property.name}" => result_list.push(${property.ident}_declaration(
+ match longhands::${property.ident}::parse_declared(value, base_url) {
+ Some(value) => value,
+ None => return InvalidValue,
+ }
+ )),
+ % else:
+ "${property.name}" => {}
+ % endif
% endfor
% for shorthand in SHORTHANDS:
"${shorthand.name}" => match CSSWideKeyword::parse(value) {
@@ -1254,26 +1469,44 @@ fn cascade_with_cached_declarations(applicable_declarations: &[MatchedProperty],
% for style_struct in STYLE_STRUCTS:
% if style_struct.inherited:
% for property in style_struct.longhands:
- ${property.ident}_declaration(ref declared_value) => {
- style_${style_struct.name}.get_mut().${property.ident} =
- match *declared_value {
- SpecifiedValue(ref specified_value)
- => longhands::${property.ident}::to_computed_value(
- (*specified_value).clone(),
- context
- ),
- CSSWideKeyword(Initial)
- => longhands::${property.ident}::get_initial_value(),
- CSSWideKeyword(Inherit) => {
- // This is a bit slow, but this is rare so it shouldn't matter.
- // FIXME: is it still?
- parent_style.${style_struct.name}
- .get()
- .${property.ident}
- .clone()
- }
+ % if property.derived_from is None:
+ ${property.ident}_declaration(ref declared_value) => {
+ let computed_value = match *declared_value {
+ SpecifiedValue(ref specified_value)
+ => longhands::${property.ident}::to_computed_value(
+ (*specified_value).clone(),
+ context
+ ),
+ CSSWideKeyword(Initial)
+ => longhands::${property.ident}::get_initial_value(),
+ CSSWideKeyword(Inherit) => {
+ // This is a bit slow, but this is rare so it shouldn't
+ // matter.
+ //
+ // FIXME: is it still?
+ parent_style.${style_struct.name}
+ .get()
+ .${property.ident}
+ .clone()
+ }
+ };
+ style_${style_struct.name}.get_mut().${property.ident} =
+ computed_value;
+
+ % if property.name in DERIVED_LONGHANDS:
+ % for derived in DERIVED_LONGHANDS[property.name]:
+ style_${derived.style_struct.name}.get_mut()
+ .${derived.ident} =
+ longhands::${derived.ident}::derive(computed_value,
+ context);
+ % endfor
+ % endif
}
- }
+ % else:
+ ${property.ident}_declaration(_) => {
+ // Ignore derived properties; they cannot be set by content.
+ }
+ % endif
% endfor
% endif
% endfor
@@ -1325,8 +1558,13 @@ pub fn cascade(applicable_declarations: &[MatchedProperty],
is_root_element: is_root_element,
inherited_font_weight: inherited_font_style.font_weight,
inherited_font_size: inherited_font_style.font_size,
+ inherited_height: inherited_style.Box.get().height,
+ inherited_minimum_line_height: inherited_style.InheritedBox
+ .get()
+ ._servo_minimum_line_height,
// To be overridden by applicable declarations:
font_size: inherited_font_style.font_size,
+ display: longhands::display::get_initial_value(),
color: inherited_style.Color.get().color,
positioned: false,
floated: false,
@@ -1363,6 +1601,9 @@ pub fn cascade(applicable_declarations: &[MatchedProperty],
color_declaration(ref value) => {
context.color = get_specified!(Color, color, value);
}
+ display_declaration(ref value) => {
+ context.display = get_specified!(Box, display, value);
+ }
position_declaration(ref value) => {
context.positioned = match get_specified!(Box, position, value) {
longhands::position::absolute | longhands::position::fixed => true,
@@ -1414,27 +1655,45 @@ pub fn cascade(applicable_declarations: &[MatchedProperty],
match *declaration {
% for style_struct in STYLE_STRUCTS:
% for property in style_struct.longhands:
- ${property.ident}_declaration(ref declared_value) => {
- style_${style_struct.name}.get_mut().${property.ident} =
- match *declared_value {
- SpecifiedValue(ref specified_value)
- => longhands::${property.ident}::to_computed_value(
- (*specified_value).clone(),
- &context
- ),
- CSSWideKeyword(Initial)
- => longhands::${property.ident}::get_initial_value(),
- CSSWideKeyword(Inherit) => {
- // This is a bit slow, but this is rare so it shouldn't matter.
- // FIXME: is it still?
- cacheable = false;
- inherited_style.${style_struct.name}
- .get()
- .${property.ident}
- .clone()
- }
+ % if property.derived_from is None:
+ ${property.ident}_declaration(ref declared_value) => {
+ let computed_value = match *declared_value {
+ SpecifiedValue(ref specified_value)
+ => longhands::${property.ident}::to_computed_value(
+ (*specified_value).clone(),
+ &context
+ ),
+ CSSWideKeyword(Initial)
+ => longhands::${property.ident}::get_initial_value(),
+ CSSWideKeyword(Inherit) => {
+ // This is a bit slow, but this is rare so it shouldn't
+ // matter.
+ //
+ // FIXME: is it still?
+ cacheable = false;
+ inherited_style.${style_struct.name}
+ .get()
+ .${property.ident}
+ .clone()
+ }
+ };
+ style_${style_struct.name}.get_mut().${property.ident} =
+ computed_value;
+
+ % if property.name in DERIVED_LONGHANDS:
+ % for derived in DERIVED_LONGHANDS[property.name]:
+ style_${derived.style_struct.name}.get_mut()
+ .${derived.ident} =
+ longhands::${derived.ident}::derive(computed_value,
+ &context);
+ % endfor
+ % endif
}
- }
+ % else:
+ ${property.ident}_declaration(_) => {
+ // Ignore derived properties; they cannot be set by content.
+ }
+ % endif
% endfor
% endfor
}
diff --git a/src/components/util/geometry.rs b/src/components/util/geometry.rs
index e02bb8a6bb9..248ce1ba01e 100644
--- a/src/components/util/geometry.rs
+++ b/src/components/util/geometry.rs
@@ -300,3 +300,11 @@ pub fn to_pt(au: Au) -> f64 {
(a as f64) / 60f64 * 72f64 / 96f64
}
+/// Returns true if the rect contains the given point. Points on the top or left sides of the rect
+/// are considered inside the rectangle, while points on the right or bottom sides of the rect are
+/// not considered inside the rectangle.
+pub fn rect_contains_point<T:Ord + Add<T,T>>(rect: Rect<T>, point: Point2D<T>) -> bool {
+ point.x >= rect.origin.x && point.x < rect.origin.x + rect.size.width &&
+ point.y >= rect.origin.y && point.y < rect.origin.y + rect.size.height
+}
+
diff --git a/src/components/util/smallvec.rs b/src/components/util/smallvec.rs
index ab51fd961f1..84e3d55b347 100644
--- a/src/components/util/smallvec.rs
+++ b/src/components/util/smallvec.rs
@@ -60,6 +60,16 @@ pub trait SmallVec<T> : SmallVecPrivate<T> {
}
}
+ fn mut_iter<'a>(&'a mut self) -> SmallVecMutIterator<'a,T> {
+ unsafe {
+ SmallVecMutIterator {
+ ptr: cast::transmute(self.begin()),
+ end: cast::transmute(self.end()),
+ lifetime: None,
+ }
+ }
+ }
+
/// NB: For efficiency reasons (avoiding making a second copy of the inline elements), this
/// actually clears out the original array instead of moving it.
fn move_iter<'a>(&'a mut self) -> SmallVecMoveIterator<'a,T> {
@@ -94,6 +104,12 @@ pub trait SmallVec<T> : SmallVecPrivate<T> {
}
}
+ fn push_all_move<V:SmallVec<T>>(&mut self, mut other: V) {
+ for value in other.move_iter() {
+ self.push(value)
+ }
+ }
+
fn grow(&mut self, new_cap: uint) {
unsafe {
let new_alloc: *mut T = cast::transmute(global_heap::malloc_raw(mem::size_of::<T>() *
@@ -194,6 +210,30 @@ impl<'a,T> Iterator<&'a T> for SmallVecIterator<'a,T> {
}
}
+pub struct SmallVecMutIterator<'a,T> {
+ priv ptr: *mut T,
+ priv end: *mut T,
+ priv lifetime: Option<&'a mut T>
+}
+
+impl<'a,T> Iterator<&'a mut T> for SmallVecMutIterator<'a,T> {
+ #[inline]
+ fn next(&mut self) -> Option<&'a mut T> {
+ unsafe {
+ if self.ptr == self.end {
+ return None
+ }
+ let old = self.ptr;
+ self.ptr = if mem::size_of::<T>() == 0 {
+ cast::transmute(self.ptr as uint + 1)
+ } else {
+ self.ptr.offset(1)
+ };
+ Some(cast::transmute(old))
+ }
+ }
+}
+
pub struct SmallVecMoveIterator<'a,T> {
priv allocation: Option<*mut u8>,
priv iter: SmallVecIterator<'static,T>,
diff --git a/src/support/azure/rust-azure b/src/support/azure/rust-azure
-Subproject cca153ff06af97b3a66586be65322ca3196e60f
+Subproject 10b1cf8e35fa1757bcab11dcc284f77e4ceeaff
diff --git a/src/support/layers/rust-layers b/src/support/layers/rust-layers
-Subproject 400a01307515def5dd6874ec44ed8a017ada36f
+Subproject 79e405fa59c052ff78d7a2527a92474a32ac9b4
diff --git a/src/test/ref/background_external_stylesheet.html b/src/test/ref/background_external_stylesheet.html
index 791bfa41c87..9465a1b4cae 100644
--- a/src/test/ref/background_external_stylesheet.html
+++ b/src/test/ref/background_external_stylesheet.html
@@ -5,6 +5,6 @@
<link rel="stylesheet" href="subdirectory/background_image.css">
</head>
<body>
-<div class="test" style="width:200px; height:200px;"></div>
+<div class="test" style="width:206px; height:206px;"></div>
</body>
</html>
diff --git a/src/test/ref/background_position_a.html b/src/test/ref/background_position_a.html
new file mode 100644
index 00000000000..23547b3f6b6
--- /dev/null
+++ b/src/test/ref/background_position_a.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>You see here a scroll labeled FOOBIE BLETCH.</title>
+<style>
+#foo {
+ background: url(400x400_green.png);
+ background-position: 128px 0px;
+ background-repeat: no-repeat;
+ width: 528px;
+ height: 400px;
+ margin-left: 0;
+}
+</style>
+</head>
+<body>
+<div id=foo></div>
+</body>
+</html>
+
diff --git a/src/test/ref/background_position_b.html b/src/test/ref/background_position_b.html
new file mode 100644
index 00000000000..6c1d0ab9ecd
--- /dev/null
+++ b/src/test/ref/background_position_b.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>You see here a scroll labeled FOOBIE BLETCH.</title>
+<style>
+#foo {
+ background: url(400x400_green.png);
+ width: 400px;
+ height: 400px;
+ margin-left: 128px;
+}
+</style>
+</head>
+<body>
+<div id=foo></div>
+</body>
+</html>
+
+
diff --git a/src/test/ref/background_ref.html b/src/test/ref/background_ref.html
index 11faa177fb7..81f0d15e1fc 100644
--- a/src/test/ref/background_ref.html
+++ b/src/test/ref/background_ref.html
@@ -4,6 +4,6 @@
<title></title>
</head>
<body>
-<img class="test" src="rust-0.png" style="width:200px; height:200px;" />
+<img class="test" src="rust-0.png" style="width:206px; height:206px;" />
</body>
</html>
diff --git a/src/test/ref/background_repeat_both_a.html b/src/test/ref/background_repeat_both_a.html
new file mode 100644
index 00000000000..2f5bb3353ce
--- /dev/null
+++ b/src/test/ref/background_repeat_both_a.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>You see here a scroll labeled XIXAXA XOXAXA XUXAXA.</title>
+<style>
+div {
+ width: 412px;
+ height: 412px;
+ background: url(rust-0.png);
+ background-repeat: repeat;
+}
+</style>
+</head>
+<body>
+<div id=repeat></div>
+</body>
+</html>
+
+
diff --git a/src/test/ref/background_repeat_both_b.html b/src/test/ref/background_repeat_both_b.html
new file mode 100644
index 00000000000..4a452813855
--- /dev/null
+++ b/src/test/ref/background_repeat_both_b.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>You see here a scroll labeled XIXAXA XOXAXA XUXAXA.</title>
+<style>
+.repeaty {
+ width: 412px;
+ height: 206px;
+ background: url(rust-0.png);
+}
+.repeatx {
+ width: 206px;
+ height: 206px;
+ background: url(rust-0.png);
+ float: left;
+}
+</style>
+</head>
+<body>
+<div>
+<div class=repeaty><div class=repeatx></div><div class=repeatx></div></div>
+<div class=repeaty><div class=repeatx></div><div class=repeatx></div></div>
+</div>
+</body>
+</html>
+
+
diff --git a/src/test/ref/background_repeat_none_a.html b/src/test/ref/background_repeat_none_a.html
new file mode 100644
index 00000000000..81fadc33961
--- /dev/null
+++ b/src/test/ref/background_repeat_none_a.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>You see here a scroll labeled XIXAXA XOXAXA XUXAXA.</title>
+<style>
+div {
+ width: 400px;
+ height: 400px;
+ background: url(rust-0.png);
+ background-repeat: no-repeat;
+}
+</style>
+</head>
+<body>
+<div id=repeat></div>
+</body>
+</html>
+
+
diff --git a/src/test/ref/background_repeat_none_b.html b/src/test/ref/background_repeat_none_b.html
new file mode 100644
index 00000000000..4448cf19eb9
--- /dev/null
+++ b/src/test/ref/background_repeat_none_b.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>You see here a scroll labeled XIXAXA XOXAXA XUXAXA.</title>
+<style>
+div {
+ width: 200px;
+ height: 200px;
+ background: url(rust-0.png);
+}
+</style>
+</head>
+<body>
+<div id=repeat></div>
+</body>
+</html>
+
+
diff --git a/src/test/ref/background_repeat_x_a.html b/src/test/ref/background_repeat_x_a.html
new file mode 100644
index 00000000000..eb944aa9751
--- /dev/null
+++ b/src/test/ref/background_repeat_x_a.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>You see here a scroll labeled XIXAXA XOXAXA XUXAXA.</title>
+<style>
+div {
+ width: 412px;
+ height: 412px;
+ background: url(rust-0.png);
+ background-repeat: repeat-x;
+ position: absolute;
+ left: 0;
+ top: 0;
+}
+</style>
+</head>
+<body>
+<div></div>
+</body>
+</html>
+
+
diff --git a/src/test/ref/background_repeat_x_b.html b/src/test/ref/background_repeat_x_b.html
new file mode 100644
index 00000000000..410bcad4dd7
--- /dev/null
+++ b/src/test/ref/background_repeat_x_b.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>You see here a scroll labeled XIXAXA XOXAXA XUXAXA.</title>
+<style>
+div {
+ width: 206px;
+ height: 206px;
+ background: url(rust-0.png);
+ position: absolute;
+ top: 0;
+}
+.repeata {
+ left: 0;
+}
+.repeatb {
+ left: 206px;
+}
+</style>
+</head>
+<body>
+<div class=repeata></div><div class=repeatb></div>
+</body>
+</html>
+
+
diff --git a/src/test/ref/background_repeat_y_a.html b/src/test/ref/background_repeat_y_a.html
new file mode 100644
index 00000000000..46842c9501c
--- /dev/null
+++ b/src/test/ref/background_repeat_y_a.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>You see here a scroll labeled XIXAXA XOXAXA XUXAXA.</title>
+<style>
+div {
+ width: 412px;
+ height: 412px;
+ background: url(rust-0.png);
+ background-repeat: repeat-y;
+}
+</style>
+</head>
+<body>
+<div id=repeat></div>
+</body>
+</html>
+
+
diff --git a/src/test/ref/background_repeat_y_b.html b/src/test/ref/background_repeat_y_b.html
new file mode 100644
index 00000000000..706e76af3c6
--- /dev/null
+++ b/src/test/ref/background_repeat_y_b.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>You see here a scroll labeled XIXAXA XOXAXA XUXAXA.</title>
+<style>
+.repeaty {
+ width: 206px;
+ height: 206px;
+ background: url(rust-0.png);
+}
+</style>
+</head>
+<body>
+<div>
+<div class=repeaty></div>
+<div class=repeaty></div>
+</div>
+</body>
+</html>
+
+
diff --git a/src/test/ref/background_style_attr.html b/src/test/ref/background_style_attr.html
index 28bc8f6b21d..1879b52816b 100644
--- a/src/test/ref/background_style_attr.html
+++ b/src/test/ref/background_style_attr.html
@@ -4,6 +4,6 @@
<title></title>
</head>
<body>
-<div class="test" style="background: url(rust-0.png); width:200px; height:200px;"></div>
+<div class="test" style="background: url(rust-0.png); width:206px; height:206px;"></div>
</body>
</html>
diff --git a/src/test/ref/basic.list b/src/test/ref/basic.list
index 8108ab7a9c9..4df86c52e8b 100644
--- a/src/test/ref/basic.list
+++ b/src/test/ref/basic.list
@@ -48,9 +48,20 @@
== position_abs_replaced_simple_a.html position_abs_replaced_simple_b.html
== position_abs_static_y_a.html position_abs_static_y_b.html
== position_abs_width_percentage_a.html position_abs_width_percentage_b.html
-== position_fixed_a.html position_fixed_b.html
-== position_fixed_simple_a.html position_fixed_simple_b.html
-== position_fixed_static_y_a.html position_fixed_static_y_b.html
+# commented out because multiple layers don't work with reftests --pcwalton
+# == position_fixed_a.html position_fixed_b.html
+# == position_fixed_simple_a.html position_fixed_simple_b.html
+# == position_fixed_static_y_a.html position_fixed_static_y_b.html
== position_relative_a.html position_relative_b.html
== position_relative_top_percentage_a.html position_relative_top_percentage_b.html
== background_none_a.html background_none_b.html
+== negative_margins_a.html negative_margins_b.html
+== negative_margin_uncle_a.html negative_margin_uncle_b.html
+== inline_padding_a.html inline_padding_b.html
+== min_max_height_a.html min_max_height_b.html
+== minimum_line_height_a.html minimum_line_height_b.html
+== background_position_a.html background_position_b.html
+== background_repeat_x_a.html background_repeat_x_b.html
+== background_repeat_y_a.html background_repeat_y_b.html
+== background_repeat_none_a.html background_repeat_none_b.html
+== background_repeat_both_a.html background_repeat_both_b.html
diff --git a/src/test/ref/inline_padding_a.html b/src/test/ref/inline_padding_a.html
new file mode 100644
index 00000000000..3caceb80cd6
--- /dev/null
+++ b/src/test/ref/inline_padding_a.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>You see here a scroll labeled READ ME.</title>
+<style>
+#a {
+ padding: 0 0 0 64px;
+ color: blue;
+}
+</style>
+</head>
+<body>
+<div><span id=a>blah blah blah</span></div>
+</body>
+</html>
+
diff --git a/src/test/ref/inline_padding_b.html b/src/test/ref/inline_padding_b.html
new file mode 100644
index 00000000000..101efc121c4
--- /dev/null
+++ b/src/test/ref/inline_padding_b.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>You see here a scroll labeled READ ME.</title>
+<style>
+#a {
+ padding: 0 0 0 64px;
+ color: blue;
+ margin: 0;
+}
+</style>
+</head>
+<body>
+<div id=a>blah blah blah</div>
+</body>
+</html>
+
diff --git a/src/test/ref/min_max_height_a.html b/src/test/ref/min_max_height_a.html
new file mode 100644
index 00000000000..ace1cac6df3
--- /dev/null
+++ b/src/test/ref/min_max_height_a.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>You see here a scroll labeled VE FORBRYDERNE.</title>
+<style>
+div {
+ width: 50px;
+}
+#a {
+ background: red;
+ height: 50px;
+ min-height: 100px;
+}
+#b {
+ background: green;
+ height: 100px;
+ max-height: 50px;
+}
+#c {
+ background: blue;
+ height: 50px;
+ min-height: 100px; /* <-- this one overrides per the spec */
+ max-height: 25px;
+}
+</style>
+</head>
+<body>
+<div id=a></div>
+<div id=b></div>
+<div id=c></div>
+</body>
+</html>
+
diff --git a/src/test/ref/min_max_height_b.html b/src/test/ref/min_max_height_b.html
new file mode 100644
index 00000000000..8723d65b258
--- /dev/null
+++ b/src/test/ref/min_max_height_b.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>You see here a scroll labeled VE FORBRYDERNE.</title>
+<style>
+div {
+ width: 50px;
+}
+#a {
+ background: red;
+ height: 100px;
+}
+#b {
+ background: green;
+ height: 50px;
+}
+#c {
+ background: blue;
+ height: 100px;
+}
+</style>
+</head>
+<body>
+<div id=a></div>
+<div id=b></div>
+<div id=c></div>
+</body>
+</html>
+
diff --git a/src/test/ref/minimum_line_height_a.html b/src/test/ref/minimum_line_height_a.html
new file mode 100644
index 00000000000..819c4ecdc8f
--- /dev/null
+++ b/src/test/ref/minimum_line_height_a.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+#a {
+ line-height: 100px;
+}
+
+#b {
+ line-height: 6px;
+}
+</style>
+<body>
+<div id=a><span id=b>Four score and seven years ago our fathers brought forth on this continent, a new nation, conceived in Liberty, and dedicated to the proposition that all men are created equal. Now we are engaged in a great civil war, testing whether that nation, or any nation so conceived and so dedicated, can long endure. We are met on a great battle-field of that war. We have come to dedicate a portion of that field, as a final resting place for those who here gave their lives that that nation might live. It is altogether fitting and proper that we should do this.</span></div>
+</body>
+</html>
+
diff --git a/src/test/ref/minimum_line_height_b.html b/src/test/ref/minimum_line_height_b.html
new file mode 100644
index 00000000000..3805578b1ec
--- /dev/null
+++ b/src/test/ref/minimum_line_height_b.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+#a {
+ line-height: 100px;
+}
+
+#b {
+ line-height: 100px;
+}
+</style>
+<body>
+<div id=a><span id=b>Four score and seven years ago our fathers brought forth on this continent, a new nation, conceived in Liberty, and dedicated to the proposition that all men are created equal. Now we are engaged in a great civil war, testing whether that nation, or any nation so conceived and so dedicated, can long endure. We are met on a great battle-field of that war. We have come to dedicate a portion of that field, as a final resting place for those who here gave their lives that that nation might live. It is altogether fitting and proper that we should do this.</span></div>
+</body>
+</html>
+
diff --git a/src/test/ref/negative_margin_uncle_a.html b/src/test/ref/negative_margin_uncle_a.html
new file mode 100644
index 00000000000..a96c2bdd700
--- /dev/null
+++ b/src/test/ref/negative_margin_uncle_a.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>You see here a scroll labeled KERNOD WEL.</title>
+<style>
+body {
+ font-size: 40px;
+}
+#d {
+ float: right;
+ background: green;
+ color: white;
+}
+#b {
+ margin: 0 0 -100px 0;
+}
+#c {
+ margin: 100px 0 0 0;
+ clear: both;
+ background-color: blue;
+ color: white;
+}
+</style>
+</head>
+<body>
+<div id=d>Beetlejuice</div>
+<div id=a><div id=b>Beetlejuice</div></div>
+<div id=c>Beetlejuice</div>
+</body>
+</html>
+
diff --git a/src/test/ref/negative_margin_uncle_b.html b/src/test/ref/negative_margin_uncle_b.html
new file mode 100644
index 00000000000..3269c47c2d6
--- /dev/null
+++ b/src/test/ref/negative_margin_uncle_b.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>You see here a scroll labeled KERNOD WEL.</title>
+<style>
+body {
+ font-size: 40px;
+}
+#d {
+ float: right;
+ background: green;
+ color: white;
+}
+#c {
+ clear: both;
+ background-color: blue;
+ color: white;
+}
+</style>
+</head>
+<body>
+<div id=d>Beetlejuice</div>
+<div id=a>Beetlejuice</div>
+<div id=c>Beetlejuice</div>
+</body>
+</html>
+
diff --git a/src/test/ref/negative_margins_a.html b/src/test/ref/negative_margins_a.html
new file mode 100644
index 00000000000..3f4197f0e4a
--- /dev/null
+++ b/src/test/ref/negative_margins_a.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>You see here a scroll labeled JUYED AWK YACC.</title>
+<style>
+* {
+ line-height: 14px;
+}
+#b {
+ margin-top: -14px;
+}
+</style>
+</head>
+<body>
+<div id=a>Here lies the body of Jonathan Blake.</div>
+<div id=b>Stepped on the gas instead of the brake.</div>
+</body>
+</html>
+
diff --git a/src/test/ref/negative_margins_b.html b/src/test/ref/negative_margins_b.html
new file mode 100644
index 00000000000..092ad0316d3
--- /dev/null
+++ b/src/test/ref/negative_margins_b.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>You see here a scroll labeled JUYED AWK YACC.</title>
+<style>
+* {
+ line-height: 14px;
+}
+#a {
+ position: relative;
+}
+#b {
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+}
+</style>
+</head>
+<body>
+<div id=a>Here lies the body of Jonathan Blake.
+<div id=b>Stepped on the gas instead of the brake.</div></div>
+</body>
+</html>
+