aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--components/gfx/display_list/mod.rs470
-rw-r--r--components/gfx/display_list/optimizer.rs64
-rw-r--r--components/gfx/lib.rs1
-rw-r--r--components/gfx/render_context.rs4
-rw-r--r--components/gfx/render_task.rs144
-rw-r--r--components/layout/block.rs93
-rw-r--r--components/layout/display_list_builder.rs305
-rw-r--r--components/layout/flow.rs59
-rw-r--r--components/layout/fragment.rs26
-rw-r--r--components/layout/inline.rs47
-rw-r--r--components/layout/layout_task.rs107
-rw-r--r--components/util/dlist.rs27
-rw-r--r--components/util/geometry.rs5
13 files changed, 807 insertions, 545 deletions
diff --git a/components/gfx/display_list/mod.rs b/components/gfx/display_list/mod.rs
index 55782463436..88413b350a8 100644
--- a/components/gfx/display_list/mod.rs
+++ b/components/gfx/display_list/mod.rs
@@ -15,7 +15,8 @@
//! low-level drawing primitives.
use color::Color;
-use render_context::RenderContext;
+use display_list::optimizer::DisplayListOptimizer;
+use render_context::{RenderContext, ToAzureRect};
use text::glyph::CharIndex;
use text::TextRun;
@@ -23,11 +24,16 @@ use azure::azure::AzFloat;
use collections::dlist::{mod, DList};
use geom::{Point2D, Rect, SideOffsets2D, Size2D, Matrix2D};
use libc::uintptr_t;
+use render_task::RenderLayer;
+use script_traits::UntrustedNodeAddress;
+use servo_msg::compositor_msg::LayerId;
use servo_net::image::base::Image;
use servo_util::dlist as servo_dlist;
-use servo_util::geometry::Au;
+use servo_util::geometry::{mod, Au};
use servo_util::range::Range;
+use servo_util::smallvec::{SmallVec, SmallVec8};
use std::fmt;
+use std::mem;
use std::slice::Items;
use style::computed_values::border_style;
use sync::Arc;
@@ -55,240 +61,308 @@ impl OpaqueNode {
}
}
-/// "Steps" as defined by CSS 2.1 § E.2.
-#[deriving(Clone, PartialEq, Show)]
-pub enum StackingLevel {
+/// Display items that make up a stacking context. "Steps" here refer to the steps in CSS 2.1
+/// Appendix E.
+///
+/// TODO(pcwalton): We could reduce the size of this structure with a more "skip list"-like
+/// structure, omitting several pointers and lengths.
+pub struct DisplayList {
/// The border and backgrounds for the root of this stacking context: steps 1 and 2.
- BackgroundAndBordersStackingLevel,
+ pub background_and_borders: DList<DisplayItem>,
/// Borders and backgrounds for block-level descendants: step 4.
- BlockBackgroundsAndBordersStackingLevel,
+ pub block_backgrounds_and_borders: DList<DisplayItem>,
/// Floats: step 5. These are treated as pseudo-stacking contexts.
- FloatStackingLevel,
+ pub floats: DList<DisplayItem>,
/// All other content.
- ContentStackingLevel,
- /// 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.
- PositionedDescendantStackingLevel(i32)
+ pub content: DList<DisplayItem>,
+ /// Child stacking contexts.
+ pub children: DList<Arc<StackingContext>>,
}
-impl StackingLevel {
+impl DisplayList {
+ /// Creates a new, empty display list.
#[inline]
- pub fn from_background_and_border_level(level: BackgroundAndBorderLevel) -> StackingLevel {
- match level {
- RootOfStackingContextLevel => BackgroundAndBordersStackingLevel,
- BlockLevel => BlockBackgroundsAndBordersStackingLevel,
- ContentLevel => ContentStackingLevel,
+ pub fn new() -> DisplayList {
+ DisplayList {
+ background_and_borders: DList::new(),
+ block_backgrounds_and_borders: DList::new(),
+ floats: DList::new(),
+ content: DList::new(),
+ children: DList::new(),
}
}
-}
-struct StackingContext {
- /// The border and backgrounds for the root of this stacking context: steps 1 and 2.
- pub background_and_borders: DisplayList,
- /// Borders and backgrounds for block-level descendants: step 4.
- pub block_backgrounds_and_borders: DisplayList,
- /// Floats: step 5. These are treated as pseudo-stacking contexts.
- pub floats: DisplayList,
- /// All other content.
- pub content: DisplayList,
- /// Positioned descendant stacking contexts, along with their `z-index` levels.
- pub positioned_descendants: Vec<(i32, DisplayList)>,
-}
-
-impl StackingContext {
- /// Creates a new empty stacking context.
+ /// Appends all display items from `other` into `self`, preserving stacking order and emptying
+ /// `other` in the process.
#[inline]
- fn new() -> StackingContext {
- StackingContext {
- background_and_borders: DisplayList::new(),
- block_backgrounds_and_borders: DisplayList::new(),
- floats: DisplayList::new(),
- content: DisplayList::new(),
- positioned_descendants: Vec::new(),
- }
+ pub fn append_from(&mut self, other: &mut DisplayList) {
+ servo_dlist::append_from(&mut self.background_and_borders,
+ &mut other.background_and_borders);
+ servo_dlist::append_from(&mut self.block_backgrounds_and_borders,
+ &mut other.block_backgrounds_and_borders);
+ servo_dlist::append_from(&mut self.floats, &mut other.floats);
+ servo_dlist::append_from(&mut self.content, &mut other.content);
+ servo_dlist::append_from(&mut self.children, &mut other.children);
}
- /// Initializes a stacking context from a display list, consuming that display list in the
- /// process.
- fn init_from_list(&mut self, list: &mut DisplayList) {
- while !list.list.is_empty() {
- let mut head = DisplayList::from_list(servo_dlist::split(&mut list.list));
- match head.front().unwrap().base().level {
- BackgroundAndBordersStackingLevel => {
- self.background_and_borders.append_from(&mut head)
- }
- BlockBackgroundsAndBordersStackingLevel => {
- self.block_backgrounds_and_borders.append_from(&mut head)
- }
- FloatStackingLevel => self.floats.append_from(&mut head),
- ContentStackingLevel => self.content.append_from(&mut head),
- PositionedDescendantStackingLevel(z_index) => {
- match self.positioned_descendants.iter_mut().find(|& &(z, _)| z_index == z) {
- Some(&(_, ref mut my_list)) => {
- my_list.append_from(&mut head);
- continue
- }
- None => {}
- }
+ /// Merges all display items from all non-float stacking levels to the `float` stacking level.
+ #[inline]
+ pub fn form_float_pseudo_stacking_context(&mut self) {
+ servo_dlist::prepend_from(&mut self.floats, &mut self.content);
+ servo_dlist::prepend_from(&mut self.floats, &mut self.block_backgrounds_and_borders);
+ servo_dlist::prepend_from(&mut self.floats, &mut self.background_and_borders);
+ }
- self.positioned_descendants.push((z_index, head))
- }
- }
+ /// Returns a list of all items in this display list concatenated together. This is extremely
+ /// inefficient and should only be used for debugging.
+ pub fn all_display_items(&self) -> Vec<DisplayItem> {
+ let mut result = Vec::new();
+ for display_item in self.background_and_borders.iter() {
+ result.push((*display_item).clone())
+ }
+ for display_item in self.block_backgrounds_and_borders.iter() {
+ result.push((*display_item).clone())
+ }
+ for display_item in self.floats.iter() {
+ result.push((*display_item).clone())
}
+ for display_item in self.content.iter() {
+ result.push((*display_item).clone())
+ }
+ result
}
}
-/// Which level to place backgrounds and borders in.
-pub enum BackgroundAndBorderLevel {
- RootOfStackingContextLevel,
- BlockLevel,
- ContentLevel,
-}
-
-/// A list of rendering operations to be performed.
-#[deriving(Clone, Show)]
-pub struct DisplayList {
- pub list: DList<DisplayItem>,
-}
-
-pub enum DisplayListIterator<'a> {
- EmptyDisplayListIterator,
- ParentDisplayListIterator(Items<'a,DisplayList>),
+/// Represents one CSS stacking context, which may or may not have a hardware layer.
+pub struct StackingContext {
+ /// The display items that make up this stacking context.
+ pub display_list: Box<DisplayList>,
+ /// The layer for this stacking context, if there is one.
+ pub layer: Option<Arc<RenderLayer>>,
+ /// The position and size of this stacking context.
+ pub bounds: Rect<Au>,
+ /// The clipping rect for this stacking context, in the coordinate system of the *parent*
+ /// stacking context.
+ pub clip_rect: Rect<Au>,
+ /// The `z-index` for this stacking context.
+ pub z_index: i32,
}
-impl<'a> Iterator<&'a DisplayList> for DisplayListIterator<'a> {
+impl StackingContext {
+ /// Creates a new stacking context.
+ ///
+ /// TODO(pcwalton): Stacking contexts should not always be clipped to their bounds, to handle
+ /// overflow properly.
#[inline]
- fn next(&mut self) -> Option<&'a DisplayList> {
- match *self {
- EmptyDisplayListIterator => None,
- ParentDisplayListIterator(ref mut subiterator) => subiterator.next(),
+ pub fn new(display_list: Box<DisplayList>,
+ bounds: Rect<Au>,
+ z_index: i32,
+ layer: Option<Arc<RenderLayer>>)
+ -> StackingContext {
+ StackingContext {
+ display_list: display_list,
+ layer: layer,
+ bounds: bounds,
+ clip_rect: bounds,
+ z_index: z_index,
}
}
-}
-impl DisplayList {
- /// Creates a new display list.
- #[inline]
- pub fn new() -> DisplayList {
- DisplayList {
- list: DList::new(),
- }
- }
+ /// Draws the stacking context in the proper order according to the steps in CSS 2.1 § E.2.
+ pub fn optimize_and_draw_into_context(&self,
+ render_context: &mut RenderContext,
+ tile_bounds: &Rect<AzFloat>,
+ current_transform: &Matrix2D<AzFloat>,
+ current_clip_stack: &mut Vec<Rect<Au>>) {
+ // Optimize the display list to throw out out-of-bounds display items and so forth.
+ let display_list = DisplayListOptimizer::new(tile_bounds).optimize(&*self.display_list);
- /// Creates a new display list from the given list of display items.
- fn from_list(list: DList<DisplayItem>) -> DisplayList {
- DisplayList {
- list: list,
+ // Sort positioned children according to z-index.
+ let mut positioned_children = SmallVec8::new();
+ for kid in display_list.children.iter() {
+ positioned_children.push((*kid).clone());
}
- }
-
- /// Appends the given item to the display list.
- #[inline]
- pub fn push(&mut self, item: DisplayItem) {
- self.list.push_back(item);
- }
+ positioned_children.as_slice_mut().sort_by(|this, other| this.z_index.cmp(&other.z_index));
- /// Appends the items in the given display list to this one, removing them in the process.
- #[inline]
- pub fn append_from(&mut self, other: &mut DisplayList) {
- servo_dlist::append_from(&mut self.list, &mut other.list)
- }
+ // Steps 1 and 2: Borders and background for the root.
+ for display_item in display_list.background_and_borders.iter() {
+ display_item.draw_into_context(render_context, current_transform, current_clip_stack)
+ }
- /// Returns the first display item in this list.
- #[inline]
- fn front(&self) -> Option<&DisplayItem> {
- self.list.front()
- }
+ // Step 3: Positioned descendants with negative z-indices.
+ for positioned_kid in positioned_children.iter() {
+ if positioned_kid.z_index >= 0 {
+ break
+ }
+ if positioned_kid.layer.is_none() {
+ let new_transform =
+ current_transform.translate(positioned_kid.bounds.origin.x.to_nearest_px()
+ as AzFloat,
+ positioned_kid.bounds.origin.y.to_nearest_px()
+ as AzFloat);
+ let new_tile_rect =
+ self.compute_tile_rect_for_child_stacking_context(tile_bounds,
+ &**positioned_kid);
+ positioned_kid.optimize_and_draw_into_context(render_context,
+ &new_tile_rect,
+ &new_transform,
+ current_clip_stack);
+ }
+ }
- pub fn debug(&self) {
- for item in self.list.iter() {
- item.debug_with_level(0);
+ // Step 4: Block backgrounds and borders.
+ for display_item in display_list.block_backgrounds_and_borders.iter() {
+ display_item.draw_into_context(render_context, current_transform, current_clip_stack)
}
- }
- /// Draws the display list into the given render context. The display list must be flattened
- /// first for correct painting.
- pub fn draw_into_context(&self,
- render_context: &mut RenderContext,
- current_transform: &Matrix2D<AzFloat>,
- current_clip_stack: &mut Vec<Rect<Au>>) {
- debug!("Beginning display list.");
- for item in self.list.iter() {
- item.draw_into_context(render_context, current_transform, current_clip_stack)
+ // Step 5: Floats.
+ for display_item in display_list.floats.iter() {
+ display_item.draw_into_context(render_context, current_transform, current_clip_stack)
}
- debug!("Ending display list.");
- }
- /// Returns a preorder iterator over the given display list.
- #[inline]
- pub fn iter<'a>(&'a self) -> DisplayItemIterator<'a> {
- ParentDisplayItemIterator(self.list.iter())
- }
+ // TODO(pcwalton): Step 6: Inlines that generate stacking contexts.
- /// Flattens a display list into a display list with a single stacking level according to the
- /// steps in CSS 2.1 § E.2.
- ///
- /// This must be called before `draw_into_context()` is for correct results.
- pub fn flatten(&mut self, resulting_level: StackingLevel) {
- // Fast paths:
- if self.list.len() == 0 {
- return
+ // Step 7: Content.
+ for display_item in display_list.content.iter() {
+ display_item.draw_into_context(render_context, current_transform, current_clip_stack)
}
- if self.list.len() == 1 {
- self.set_stacking_level(resulting_level);
- return
+
+ // Steps 8 and 9: Positioned descendants with nonnegative z-indices.
+ for positioned_kid in positioned_children.iter() {
+ if positioned_kid.z_index < 0 {
+ continue
+ }
+
+ if positioned_kid.layer.is_none() {
+ let new_transform =
+ current_transform.translate(positioned_kid.bounds.origin.x.to_nearest_px()
+ as AzFloat,
+ positioned_kid.bounds.origin.y.to_nearest_px()
+ as AzFloat);
+ let new_tile_rect =
+ self.compute_tile_rect_for_child_stacking_context(tile_bounds,
+ &**positioned_kid);
+ positioned_kid.optimize_and_draw_into_context(render_context,
+ &new_tile_rect,
+ &new_transform,
+ current_clip_stack);
+ }
}
- let mut stacking_context = StackingContext::new();
- stacking_context.init_from_list(self);
- debug_assert!(self.list.is_empty());
+ // TODO(pcwalton): Step 10: Outlines.
+ }
- // Steps 1 and 2: Borders and background for the root.
- self.append_from(&mut stacking_context.background_and_borders);
+ /// Translate the given tile rect into the coordinate system of a child stacking context.
+ fn compute_tile_rect_for_child_stacking_context(&self,
+ tile_bounds: &Rect<AzFloat>,
+ child_stacking_context: &StackingContext)
+ -> Rect<AzFloat> {
+ static ZERO_AZURE_RECT: Rect<f32> = Rect {
+ origin: Point2D {
+ x: 0.0,
+ y: 0.0,
+ },
+ size: Size2D {
+ width: 0.0,
+ height: 0.0
+ }
+ };
- // Sort positioned children according to z-index.
- stacking_context.positioned_descendants.sort_by(|&(z_index_a, _), &(z_index_b, _)| {
- z_index_a.cmp(&z_index_b)
- });
+ let child_stacking_context_bounds = child_stacking_context.bounds.to_azure_rect();
+ let tile_subrect = tile_bounds.intersection(&child_stacking_context_bounds)
+ .unwrap_or(ZERO_AZURE_RECT);
+ let offset = tile_subrect.origin - child_stacking_context_bounds.origin;
+ Rect(offset, tile_subrect.size)
+ }
- // Step 3: Positioned descendants with negative z-indices.
- for &(ref mut z_index, ref mut list) in stacking_context.positioned_descendants.iter_mut() {
- if *z_index < 0 {
- self.append_from(list)
+ /// Places all nodes containing the point of interest into `result`, topmost first. If
+ /// `topmost_only` is true, stops after placing one node into the list. `result` must be empty
+ /// upon entry to this function.
+ pub fn hit_test(&self,
+ point: Point2D<Au>,
+ result: &mut Vec<UntrustedNodeAddress>,
+ topmost_only: bool) {
+ fn hit_test_in_list<'a,I>(point: Point2D<Au>,
+ result: &mut Vec<UntrustedNodeAddress>,
+ topmost_only: bool,
+ mut iterator: I)
+ where I: Iterator<&'a DisplayItem> {
+ for item in iterator {
+ if geometry::rect_contains_point(item.base().clip_rect, point) &&
+ geometry::rect_contains_point(item.bounds(), point) {
+ result.push(item.base().node.to_untrusted_node_address());
+ if topmost_only {
+ return
+ }
+ }
}
}
- // Step 4: Block backgrounds and borders.
- self.append_from(&mut stacking_context.block_backgrounds_and_borders);
+ debug_assert!(!topmost_only || result.is_empty());
- // Step 5: Floats.
- self.append_from(&mut stacking_context.floats);
+ // Iterate through display items in reverse stacking order. Steps here refer to the
+ // painting steps in CSS 2.1 Appendix E.
+ //
+ // Steps 9 and 8: Positioned descendants with nonnegative z-indices.
+ for kid in self.display_list.children.iter().rev() {
+ if kid.z_index < 0 {
+ continue
+ }
+ kid.hit_test(point, result, topmost_only);
+ if topmost_only && !result.is_empty() {
+ return
+ }
+ }
+ // Steps 7, 5, and 4: Content, floats, and block backgrounds and borders.
+ //
// TODO(pcwalton): Step 6: Inlines that generate stacking contexts.
+ for display_list in [
+ &self.display_list.content,
+ &self.display_list.floats,
+ &self.display_list.block_backgrounds_and_borders,
+ ].iter() {
+ hit_test_in_list(point, result, topmost_only, display_list.iter().rev());
+ if topmost_only && !result.is_empty() {
+ return
+ }
+ }
- // Step 7: Content.
- self.append_from(&mut stacking_context.content);
-
- // Steps 8 and 9: Positioned descendants with nonnegative z-indices.
- for &(ref mut z_index, ref mut list) in stacking_context.positioned_descendants.iter_mut() {
- if *z_index >= 0 {
- self.append_from(list)
+ // Step 3: Positioned descendants with negative z-indices.
+ for kid in self.display_list.children.iter().rev() {
+ if kid.z_index >= 0 {
+ continue
+ }
+ kid.hit_test(point, result, topmost_only);
+ if topmost_only && !result.is_empty() {
+ return
}
}
- // TODO(pcwalton): Step 10: Outlines.
+ // Steps 2 and 1: Borders and background for the root.
+ hit_test_in_list(point,
+ result,
+ topmost_only,
+ self.display_list.background_and_borders.iter().rev())
+ }
+}
- self.set_stacking_level(resulting_level);
+/// Returns the stacking context in the given tree of stacking contexts with a specific layer ID.
+pub fn find_stacking_context_with_layer_id(this: &Arc<StackingContext>, layer_id: LayerId)
+ -> Option<Arc<StackingContext>> {
+ match this.layer {
+ Some(ref layer) if layer.id == layer_id => return Some((*this).clone()),
+ Some(_) | None => {}
}
- /// Sets the stacking level for this display list and all its subitems.
- fn set_stacking_level(&mut self, new_level: StackingLevel) {
- for item in self.list.iter_mut() {
- item.mut_base().level = new_level;
+ for kid in this.display_list.children.iter() {
+ match find_stacking_context_with_layer_id(kid, layer_id) {
+ Some(stacking_context) => return Some(stacking_context),
+ None => {}
}
}
+
+ None
}
/// One drawing command in the list.
@@ -318,9 +392,6 @@ pub struct BaseDisplayItem {
/// The originating DOM node.
pub node: OpaqueNode,
- /// The stacking level in which this display item lives.
- pub level: StackingLevel,
-
/// The rectangle to clip to.
///
/// TODO(pcwalton): Eventually, to handle `border-radius`, this will (at least) need to grow
@@ -330,12 +401,10 @@ pub struct BaseDisplayItem {
impl BaseDisplayItem {
#[inline(always)]
- pub fn new(bounds: Rect<Au>, node: OpaqueNode, level: StackingLevel, clip_rect: Rect<Au>)
- -> BaseDisplayItem {
+ pub fn new(bounds: Rect<Au>, node: OpaqueNode, clip_rect: Rect<Au>) -> BaseDisplayItem {
BaseDisplayItem {
bounds: bounds,
node: node,
- level: level,
clip_rect: clip_rect,
}
}
@@ -450,9 +519,6 @@ impl DisplayItem {
render_context: &mut RenderContext,
current_transform: &Matrix2D<AzFloat>,
current_clip_stack: &mut Vec<Rect<Au>>) {
- // This should have been flattened to the content stacking level first.
- assert!(self.base().level == ContentStackingLevel);
-
// TODO(pcwalton): This will need some tweaking to deal with more complex clipping regions.
let clip_rect = &self.base().clip_rect;
if current_clip_stack.len() == 0 || current_clip_stack.last().unwrap() != clip_rect {
@@ -464,6 +530,8 @@ impl DisplayItem {
current_clip_stack.push(*clip_rect);
}
+ render_context.draw_target.set_transform(current_transform);
+
match *self {
SolidColorDisplayItemClass(ref solid_color) => {
render_context.draw_solid_color(&solid_color.base.bounds, solid_color.color)
@@ -558,7 +626,7 @@ impl DisplayItem {
impl fmt::Show for DisplayItem {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(f, "{} @ {} ({:x}) [{}]",
+ write!(f, "{} @ {} ({:x})",
match *self {
SolidColorDisplayItemClass(_) => "SolidColor",
TextDisplayItemClass(_) => "Text",
@@ -569,9 +637,25 @@ impl fmt::Show for DisplayItem {
PseudoDisplayItemClass(_) => "Pseudo",
},
self.base().bounds,
- self.base().node.id(),
- self.base().level
+ self.base().node.id()
)
}
}
+pub trait OpaqueNodeMethods {
+ /// 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 to_untrusted_node_address(&self) -> UntrustedNodeAddress {
+ unsafe {
+ let OpaqueNode(addr) = *self;
+ let addr: UntrustedNodeAddress = mem::transmute(addr);
+ addr
+ }
+ }
+}
+
diff --git a/components/gfx/display_list/optimizer.rs b/components/gfx/display_list/optimizer.rs
index 69cdc469de3..9ce3fbfc3f1 100644
--- a/components/gfx/display_list/optimizer.rs
+++ b/components/gfx/display_list/optimizer.rs
@@ -2,52 +2,66 @@
* 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 display_list::{DisplayItem, DisplayList};
+//! Transforms a display list to produce a visually-equivalent, but cheaper-to-render, one.
+
+use display_list::{DisplayItem, DisplayList, StackingContext};
use collections::dlist::DList;
use geom::rect::Rect;
-use servo_util::geometry::Au;
+use servo_util::geometry::{mod, Au};
use sync::Arc;
+/// Transforms a display list to produce a visually-equivalent, but cheaper-to-render, one.
pub struct DisplayListOptimizer {
- display_list: Arc<DisplayList>,
/// The visible rect in page coordinates.
visible_rect: Rect<Au>,
}
impl DisplayListOptimizer {
- /// `visible_rect` specifies the visible rect in page coordinates.
- pub fn new(display_list: Arc<DisplayList>, visible_rect: Rect<Au>) -> DisplayListOptimizer {
+ /// Creates a new display list optimizer object. `visible_rect` specifies the visible rect in
+ /// page coordinates.
+ pub fn new(visible_rect: &Rect<f32>) -> DisplayListOptimizer {
DisplayListOptimizer {
- display_list: display_list,
- visible_rect: visible_rect,
+ visible_rect: geometry::f32_rect_to_au_rect(*visible_rect),
}
}
- pub fn optimize(self) -> DisplayList {
- self.process_display_list(&*self.display_list)
+ /// Optimizes the given display list, returning an equivalent, but cheaper-to-paint, one.
+ pub fn optimize(self, display_list: &DisplayList) -> DisplayList {
+ let mut result = DisplayList::new();
+ self.add_in_bounds_display_items(&mut result.background_and_borders,
+ display_list.background_and_borders.iter());
+ self.add_in_bounds_display_items(&mut result.block_backgrounds_and_borders,
+ display_list.block_backgrounds_and_borders.iter());
+ self.add_in_bounds_display_items(&mut result.floats, display_list.floats.iter());
+ self.add_in_bounds_display_items(&mut result.content, display_list.content.iter());
+ self.add_in_bounds_stacking_contexts(&mut result.children, display_list.children.iter());
+ result
}
- fn process_display_list(&self, display_list: &DisplayList) -> DisplayList {
- let mut result = DList::new();
- for item in display_list.iter() {
- match self.process_display_item(item) {
- None => {}
- Some(display_item) => result.push_back(display_item),
+ /// Adds display items that intersect the visible rect to `result_list`.
+ fn add_in_bounds_display_items<'a,I>(&self,
+ result_list: &mut DList<DisplayItem>,
+ mut display_items: I)
+ where I: Iterator<&'a DisplayItem> {
+ for display_item in display_items {
+ if self.visible_rect.intersects(&display_item.base().bounds) &&
+ self.visible_rect.intersects(&display_item.base().clip_rect) {
+ result_list.push_back((*display_item).clone())
}
}
- DisplayList {
- list: result,
- }
}
- fn process_display_item(&self, display_item: &DisplayItem) -> Option<DisplayItem> {
- // Eliminate display items outside the visible region.
- if !self.visible_rect.intersects(&display_item.base().bounds) ||
- !self.visible_rect.intersects(&display_item.base().clip_rect) {
- None
- } else {
- Some((*display_item).clone())
+ /// Adds child stacking contexts whose boundaries intersect the visible rect to `result_list`.
+ fn add_in_bounds_stacking_contexts<'a,I>(&self,
+ result_list: &mut DList<Arc<StackingContext>>,
+ mut stacking_contexts: I)
+ where I: Iterator<&'a Arc<StackingContext>> {
+ for stacking_context in stacking_contexts {
+ if self.visible_rect.intersects(&stacking_context.bounds) &&
+ self.visible_rect.intersects(&stacking_context.clip_rect) {
+ result_list.push_back((*stacking_context).clone())
+ }
}
}
}
diff --git a/components/gfx/lib.rs b/components/gfx/lib.rs
index c267e139fe1..701390e072c 100644
--- a/components/gfx/lib.rs
+++ b/components/gfx/lib.rs
@@ -20,6 +20,7 @@ extern crate native;
extern crate rustrt;
extern crate stb_image;
extern crate png;
+extern crate script_traits;
extern crate serialize;
extern crate unicode;
#[phase(plugin)]
diff --git a/components/gfx/render_context.rs b/components/gfx/render_context.rs
index c8c1df2cc63..ea7771efb9d 100644
--- a/components/gfx/render_context.rs
+++ b/components/gfx/render_context.rs
@@ -467,7 +467,7 @@ impl<'a> RenderContext<'a> {
}
}
-trait ToAzurePoint {
+pub trait ToAzurePoint {
fn to_azure_point(&self) -> Point2D<AzFloat>;
}
@@ -477,7 +477,7 @@ impl ToAzurePoint for Point2D<Au> {
}
}
-trait ToAzureRect {
+pub trait ToAzureRect {
fn to_azure_rect(&self) -> Rect<AzFloat>;
}
diff --git a/components/gfx/render_task.rs b/components/gfx/render_task.rs
index e1d190ac5fa..6dc3335faff 100644
--- a/components/gfx/render_task.rs
+++ b/components/gfx/render_task.rs
@@ -5,8 +5,7 @@
//! The task that handles all rendering/painting.
use buffer_map::BufferMap;
-use display_list::optimizer::DisplayListOptimizer;
-use display_list::DisplayList;
+use display_list::{mod, StackingContext};
use font_cache_task::FontCacheTask;
use font_context::FontContext;
use render_context::RenderContext;
@@ -27,9 +26,9 @@ use servo_msg::compositor_msg::{LayerMetadata, RenderListener, RenderingRenderSt
use servo_msg::constellation_msg::{ConstellationChan, Failure, FailureMsg, PipelineId};
use servo_msg::constellation_msg::{RendererReadyMsg};
use servo_msg::platform::surface::NativeSurfaceAzureMethods;
-use servo_util::geometry;
+use servo_util::geometry::{Au, ZERO_POINT};
use servo_util::opts;
-use servo_util::smallvec::{SmallVec, SmallVec1};
+use servo_util::smallvec::SmallVec;
use servo_util::task::spawn_named_with_send_on_failure;
use servo_util::task_state;
use servo_util::time::{TimeProfilerChan, profile};
@@ -39,21 +38,28 @@ use std::mem;
use std::task::TaskBuilder;
use sync::Arc;
-/// Information about a layer that layout sends to the painting task.
+/// Information about a hardware graphics layer that layout sends to the painting task.
#[deriving(Clone)]
pub struct RenderLayer {
/// A per-pipeline ID describing this layer that should be stable across reflows.
pub id: LayerId,
- /// The display list describing the contents of this layer.
- pub display_list: Arc<DisplayList>,
- /// The position of the layer in pixels.
- pub position: Rect<uint>,
/// The color of the background in this layer. Used for unrendered content.
pub background_color: Color,
/// The scrolling policy of this layer.
pub scroll_policy: ScrollPolicy,
}
+impl RenderLayer {
+ /// Creates a new `RenderLayer`.
+ pub fn new(id: LayerId, background_color: Color, scroll_policy: ScrollPolicy) -> RenderLayer {
+ RenderLayer {
+ id: id,
+ background_color: background_color,
+ scroll_policy: scroll_policy,
+ }
+ }
+}
+
pub struct RenderRequest {
pub buffer_requests: Vec<BufferRequest>,
pub scale: f32,
@@ -62,7 +68,7 @@ pub struct RenderRequest {
}
pub enum Msg {
- RenderInitMsg(SmallVec1<RenderLayer>),
+ RenderInitMsg(Arc<StackingContext>),
RenderMsg(Vec<RenderRequest>),
UnusedBufferMsg(Vec<Box<LayerBuffer>>),
PaintPermissionGranted,
@@ -102,8 +108,8 @@ pub struct RenderTask<C> {
/// The native graphics context.
native_graphics_context: Option<NativePaintingGraphicsContext>,
- /// The layers to be rendered.
- render_layers: SmallVec1<RenderLayer>,
+ /// The root stacking context sent to us by the layout thread.
+ root_stacking_context: Option<Arc<StackingContext>>,
/// Permission to send paint messages to the compositor
paint_permission: bool,
@@ -129,17 +135,36 @@ macro_rules! native_graphics_context(
fn initialize_layers<C>(compositor: &mut C,
pipeline_id: PipelineId,
epoch: Epoch,
- render_layers: &[RenderLayer])
+ root_stacking_context: &StackingContext)
where C: RenderListener {
- 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();
+ let mut metadata = Vec::new();
+ build(&mut metadata, root_stacking_context, &ZERO_POINT);
compositor.initialize_layers_for_pipeline(pipeline_id, metadata, epoch);
+
+ fn build(metadata: &mut Vec<LayerMetadata>,
+ stacking_context: &StackingContext,
+ page_position: &Point2D<Au>) {
+ let page_position = stacking_context.bounds.origin + *page_position;
+ match stacking_context.layer {
+ None => {}
+ Some(ref render_layer) => {
+ metadata.push(LayerMetadata {
+ id: render_layer.id,
+ position:
+ Rect(Point2D(page_position.x.to_nearest_px() as uint,
+ page_position.y.to_nearest_px() as uint),
+ Size2D(stacking_context.bounds.size.width.to_nearest_px() as uint,
+ stacking_context.bounds.size.height.to_nearest_px() as uint)),
+ background_color: render_layer.background_color,
+ scroll_policy: render_layer.scroll_policy,
+ })
+ }
+ }
+
+ for kid in stacking_context.display_list.children.iter() {
+ build(metadata, &**kid, &page_position)
+ }
+ }
}
impl<C> RenderTask<C> where C: RenderListener + Send {
@@ -170,11 +195,8 @@ impl<C> RenderTask<C> where C: RenderListener + Send {
compositor: compositor,
constellation_chan: constellation_chan,
time_profiler_chan: time_profiler_chan,
-
native_graphics_context: native_graphics_context,
-
- render_layers: SmallVec1::new(),
-
+ root_stacking_context: None,
paint_permission: false,
epoch: Epoch(0),
buffer_map: BufferMap::new(10000000),
@@ -205,9 +227,9 @@ impl<C> RenderTask<C> where C: RenderListener + Send {
loop {
match self.port.recv() {
- RenderInitMsg(render_layers) => {
+ RenderInitMsg(stacking_context) => {
self.epoch.next();
- self.render_layers = render_layers;
+ self.root_stacking_context = Some(stacking_context.clone());
if !self.paint_permission {
debug!("render_task: render ready msg");
@@ -219,7 +241,7 @@ impl<C> RenderTask<C> where C: RenderListener + Send {
initialize_layers(&mut self.compositor,
self.id,
self.epoch,
- self.render_layers.as_slice());
+ &*stacking_context);
}
RenderMsg(requests) => {
if !self.paint_permission {
@@ -254,15 +276,15 @@ impl<C> RenderTask<C> where C: RenderListener + Send {
PaintPermissionGranted => {
self.paint_permission = true;
- // 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());
+ match self.root_stacking_context {
+ None => {}
+ Some(ref stacking_context) => {
+ self.epoch.next();
+ initialize_layers(&mut self.compositor,
+ self.id,
+ self.epoch,
+ &**stacking_context);
+ }
}
}
PaintPermissionRevoked => {
@@ -327,9 +349,15 @@ impl<C> RenderTask<C> where C: RenderListener + Send {
scale: f32,
layer_id: LayerId) {
time::profile(time::PaintingCategory, None, self.time_profiler_chan.clone(), || {
- // Bail out if there is no appropriate render layer.
- let render_layer = match self.render_layers.iter().find(|layer| layer.id == layer_id) {
- Some(render_layer) => (*render_layer).clone(),
+ // Bail out if there is no appropriate stacking context.
+ let stacking_context = match self.root_stacking_context {
+ Some(ref stacking_context) => {
+ match display_list::find_stacking_context_with_layer_id(stacking_context,
+ layer_id) {
+ Some(stacking_context) => stacking_context,
+ None => return,
+ }
+ }
None => return,
};
@@ -342,7 +370,7 @@ impl<C> RenderTask<C> where C: RenderListener + Send {
let layer_buffer = self.find_or_create_layer_buffer_for_tile(&tile, scale);
self.worker_threads[thread_id].paint_tile(tile,
layer_buffer,
- render_layer.clone(),
+ stacking_context.clone(),
scale);
}
let new_buffers = Vec::from_fn(tile_count, |i| {
@@ -397,9 +425,9 @@ impl WorkerThreadProxy {
fn paint_tile(&mut self,
tile: BufferRequest,
layer_buffer: Option<Box<LayerBuffer>>,
- render_layer: RenderLayer,
+ stacking_context: Arc<StackingContext>,
scale: f32) {
- self.sender.send(PaintTileMsgToWorkerThread(tile, layer_buffer, render_layer, scale))
+ self.sender.send(PaintTileMsgToWorkerThread(tile, layer_buffer, stacking_context, scale))
}
fn get_painted_tile_buffer(&mut self) -> Box<LayerBuffer> {
@@ -443,8 +471,8 @@ impl WorkerThread {
loop {
match self.receiver.recv() {
ExitMsgToWorkerThread => break,
- PaintTileMsgToWorkerThread(tile, layer_buffer, render_layer, scale) => {
- let draw_target = self.optimize_and_paint_tile(&tile, render_layer, scale);
+ PaintTileMsgToWorkerThread(tile, layer_buffer, stacking_context, scale) => {
+ let draw_target = self.optimize_and_paint_tile(&tile, stacking_context, scale);
let buffer = self.create_layer_buffer_for_painted_tile(&tile,
layer_buffer,
draw_target,
@@ -457,21 +485,9 @@ impl WorkerThread {
fn optimize_and_paint_tile(&mut self,
tile: &BufferRequest,
- render_layer: RenderLayer,
+ stacking_context: Arc<StackingContext>,
scale: f32)
-> DrawTarget {
- // page_rect is in coordinates relative to the layer origin, but all display list
- // components are relative to the page origin. We make page_rect relative to
- // the page origin before passing it to the optimizer.
- let page_rect = tile.page_rect.translate(&Point2D(render_layer.position.origin.x as f32,
- render_layer.position.origin.y as f32));
- let page_rect_au = geometry::f32_rect_to_au_rect(page_rect);
-
- // Optimize the display list for this tile.
- let optimizer = DisplayListOptimizer::new(render_layer.display_list.clone(),
- page_rect_au);
- let display_list = optimizer.optimize();
-
let size = Size2D(tile.screen_rect.size.width as i32, tile.screen_rect.size.height as i32);
let draw_target = if !opts::get().gpu_painting {
DrawTarget::new(SkiaBackend, size, B8G8R8A8)
@@ -496,10 +512,11 @@ impl WorkerThread {
};
// Apply the translation to render the tile we want.
+ let tile_bounds = tile.page_rect;
let matrix: Matrix2D<AzFloat> = Matrix2D::identity();
let matrix = matrix.scale(scale as AzFloat, scale as AzFloat);
- let matrix = matrix.translate(-page_rect.origin.x as AzFloat,
- -page_rect.origin.y as AzFloat);
+ let matrix = matrix.translate(-tile_bounds.origin.x as AzFloat,
+ -tile_bounds.origin.y as AzFloat);
render_context.draw_target.set_transform(&matrix);
@@ -509,7 +526,10 @@ impl WorkerThread {
// Draw the display list.
profile(time::PaintingPerTileCategory, None, self.time_profiler_sender.clone(), || {
let mut clip_stack = Vec::new();
- display_list.draw_into_context(&mut render_context, &matrix, &mut clip_stack);
+ stacking_context.optimize_and_draw_into_context(&mut render_context,
+ &tile.page_rect,
+ &matrix,
+ &mut clip_stack);
render_context.draw_target.flush();
});
}
@@ -564,7 +584,7 @@ impl WorkerThread {
enum MsgToWorkerThread {
ExitMsgToWorkerThread,
- PaintTileMsgToWorkerThread(BufferRequest, Option<Box<LayerBuffer>>, RenderLayer, f32),
+ PaintTileMsgToWorkerThread(BufferRequest, Option<Box<LayerBuffer>>, Arc<StackingContext>, f32),
}
enum MsgFromWorkerThread {
diff --git a/components/layout/block.rs b/components/layout/block.rs
index 9b004bd798c..dbfde997841 100644
--- a/components/layout/block.rs
+++ b/components/layout/block.rs
@@ -30,9 +30,9 @@
use construct::FlowConstructor;
use context::LayoutContext;
use css::node_style::StyledNode;
-use display_list_builder::{BlockFlowDisplayListBuilding, FragmentDisplayListBuilding};
+use display_list_builder::{BlockFlowDisplayListBuilding, BlockLevel, FragmentDisplayListBuilding};
use floats::{ClearBoth, ClearLeft, ClearRight, FloatKind, FloatLeft, Floats, PlacementInfo};
-use flow::{BaseFlow, BlockFlowClass, FlowClass, Flow, ImmutableFlowUtils};
+use flow::{AbsolutePositionInfo, BaseFlow, BlockFlowClass, FlowClass, Flow, ImmutableFlowUtils};
use flow::{MutableFlowUtils, PreorderFlowTraversal, PostorderFlowTraversal, mut_base};
use flow;
use fragment::{Fragment, ImageFragment, InlineBlockFragment, FragmentBoundsIterator};
@@ -45,10 +45,9 @@ use table::ColumnInlineSize;
use wrapper::ThreadSafeLayoutNode;
use geom::Size2D;
-use gfx::display_list::BlockLevel;
use serialize::{Encoder, Encodable};
use servo_msg::compositor_msg::LayerId;
-use servo_util::geometry::{Au, MAX_AU};
+use servo_util::geometry::{Au, MAX_AU, MAX_RECT, ZERO_POINT};
use servo_util::logical_geometry::{LogicalPoint, LogicalRect, LogicalSize};
use servo_util::opts;
use std::cmp::{max, min};
@@ -1665,24 +1664,26 @@ impl Flow for BlockFlow {
// FIXME(#2795): Get the real container size
let container_size = Size2D::zero();
+ if self.is_root() {
+ self.base.clip_rect = MAX_RECT
+ }
+
if self.base.flags.is_absolutely_positioned() {
let position_start = self.base.position.start.to_physical(self.base.writing_mode,
container_size);
- self.base.absolute_position_info.absolute_containing_block_position =
- if self.is_fixed() {
- // The viewport is initially at (0, 0).
- position_start
- } else {
- // Absolute position of the containing block + position of absolute
- // flow w.r.t. the containing block.
- self.base.absolute_position_info.absolute_containing_block_position
- + position_start
- };
- // Set the absolute position, which will be passed down later as part
- // of containing block details for absolute descendants.
- self.base.abs_position =
- self.base.absolute_position_info.absolute_containing_block_position;
+ // Compute our position relative to the nearest ancestor stacking context. This will be
+ // passed down later as part of containing block details for absolute descendants.
+ self.base.stacking_relative_position = if self.is_fixed() {
+ // The viewport is initially at (0, 0).
+ position_start
+ } else {
+ // Absolute position of the containing block + position of absolute
+ // flow w.r.t. the containing block.
+ self.base
+ .absolute_position_info
+ .stacking_relative_position_of_absolute_containing_block + position_start
+ }
}
// For relatively-positioned descendants, the containing block formed by a block is just
@@ -1693,32 +1694,52 @@ impl Flow for BlockFlow {
.absolute_position_info
.relative_containing_block_size);
if self.is_positioned() {
- self.base.absolute_position_info.absolute_containing_block_position =
- self.base.abs_position
- + (self.generated_containing_block_rect().start
- + relative_offset).to_physical(self.base.writing_mode, container_size)
+ self.base
+ .absolute_position_info
+ .stacking_relative_position_of_absolute_containing_block =
+ self.base.stacking_relative_position +
+ (self.generated_containing_block_rect().start +
+ relative_offset).to_physical(self.base.writing_mode, container_size)
}
// Compute absolute position info for children.
- let mut absolute_position_info = self.base.absolute_position_info;
- absolute_position_info.relative_containing_block_size = self.fragment.content_box().size;
- absolute_position_info.layers_needed_for_positioned_flows =
- self.base.flags.layers_needed_for_descendants();
+ let absolute_position_info_for_children = AbsolutePositionInfo {
+ stacking_relative_position_of_absolute_containing_block:
+ if self.fragment.establishes_stacking_context() {
+ let logical_border_width = self.fragment.style().logical_border_width();
+ LogicalPoint::new(self.base.writing_mode,
+ logical_border_width.inline_start,
+ logical_border_width.block_start).to_physical(
+ self.base.writing_mode,
+ container_size)
+ } else {
+ self.base
+ .absolute_position_info
+ .stacking_relative_position_of_absolute_containing_block
+ },
+ relative_containing_block_size: self.fragment.content_box().size,
+ layers_needed_for_positioned_flows: self.base.flags.layers_needed_for_descendants(),
+ };
- // Compute the clipping rectangle for children.
- let this_position = self.base.abs_position;
- let clip_rect = self.fragment.clip_rect_for_children(self.base.clip_rect, this_position);
+ // Compute the origin and clipping rectangle for children.
+ let origin_for_children = if self.fragment.establishes_stacking_context() {
+ ZERO_POINT
+ } else {
+ self.base.stacking_relative_position
+ };
+ let clip_rect = self.fragment.clip_rect_for_children(self.base.clip_rect,
+ origin_for_children);
// Process children.
let writing_mode = self.base.writing_mode;
for kid in self.base.child_iter() {
if !flow::base(kid).flags.is_absolutely_positioned() {
let kid_base = flow::mut_base(kid);
- kid_base.abs_position =
- this_position +
+ kid_base.stacking_relative_position =
+ origin_for_children +
(kid_base.position.start + relative_offset).to_physical(writing_mode,
container_size);
- kid_base.absolute_position_info = absolute_position_info
+ kid_base.absolute_position_info = absolute_position_info_for_children
}
flow::mut_base(kid).clip_rect = clip_rect
@@ -1726,7 +1747,8 @@ impl Flow for BlockFlow {
// Process absolute descendant links.
for absolute_descendant in self.base.abs_descendants.iter() {
- flow::mut_base(absolute_descendant).absolute_position_info = absolute_position_info
+ flow::mut_base(absolute_descendant).absolute_position_info =
+ absolute_position_info_for_children
}
}
@@ -1816,9 +1838,10 @@ impl Flow for BlockFlow {
fn iterate_through_fragment_bounds(&self, iterator: &mut FragmentBoundsIterator) {
if iterator.should_process(&self.fragment) {
- let fragment_origin = self.base.child_fragment_absolute_position(&self.fragment);
+ let fragment_origin =
+ self.base.stacking_relative_position_of_child_fragment(&self.fragment);
iterator.process(&self.fragment,
- self.fragment.abs_bounds_from_origin(&fragment_origin));
+ self.fragment.stacking_relative_bounds(&fragment_origin));
}
}
}
diff --git a/components/layout/display_list_builder.rs b/components/layout/display_list_builder.rs
index 2948d678050..069493058b0 100644
--- a/components/layout/display_list_builder.rs
+++ b/components/layout/display_list_builder.rs
@@ -21,27 +21,22 @@ use fragment::{UnscannedTextFragment};
use model;
use util::{OpaqueNodeMethods, ToGfxColor};
-use collections::dlist::DList;
use geom::approxeq::ApproxEq;
use geom::{Point2D, Rect, Size2D, SideOffsets2D};
use gfx::color;
-use gfx::display_list::{BackgroundAndBorderLevel, BaseDisplayItem, BorderDisplayItem};
-use gfx::display_list::{BorderDisplayItemClass, ContentStackingLevel, DisplayList};
-use gfx::display_list::{FloatStackingLevel, GradientDisplayItem, GradientDisplayItemClass};
-use gfx::display_list::{GradientStop, ImageDisplayItem, ImageDisplayItemClass, LineDisplayItem};
-use gfx::display_list::{LineDisplayItemClass, PositionedDescendantStackingLevel};
-use gfx::display_list::{PseudoDisplayItemClass, RootOfStackingContextLevel, SidewaysLeft};
-use gfx::display_list::{SidewaysRight, SolidColorDisplayItem, SolidColorDisplayItemClass};
-use gfx::display_list::{StackingLevel, TextDisplayItem, TextDisplayItemClass, Upright};
+use gfx::display_list::{BaseDisplayItem, BorderDisplayItem, BorderDisplayItemClass, DisplayItem};
+use gfx::display_list::{DisplayList, GradientDisplayItem, GradientDisplayItemClass, GradientStop};
+use gfx::display_list::{ImageDisplayItem, ImageDisplayItemClass, LineDisplayItem};
+use gfx::display_list::{LineDisplayItemClass, PseudoDisplayItemClass, SidewaysLeft, SidewaysRight};
+use gfx::display_list::{SolidColorDisplayItem, SolidColorDisplayItemClass, StackingContext};
+use gfx::display_list::{TextDisplayItem, TextDisplayItemClass, Upright};
use gfx::render_task::RenderLayer;
use servo_msg::compositor_msg::{FixedPosition, Scrollable};
use servo_msg::constellation_msg::{ConstellationChan, FrameRectMsg};
use servo_net::image::holder::ImageHolder;
-use servo_util::dlist;
-use servo_util::geometry::{mod, Au, ZERO_RECT};
+use servo_util::geometry::{mod, Au, ZERO_POINT, ZERO_RECT};
use servo_util::logical_geometry::{LogicalRect, WritingMode};
use servo_util::opts;
-use std::mem;
use style::computed::{AngleAoc, CornerAoc, LP_Length, LP_Percentage, LengthOrPercentage};
use style::computed::{LinearGradient, LinearGradientImage, UrlImage};
use style::computed_values::{background_attachment, background_repeat, border_style, overflow};
@@ -50,12 +45,36 @@ use style::{ComputedValues, Bottom, Left, RGBA, Right, Top};
use sync::Arc;
use url::Url;
+/// The results of display list building for a single flow.
+pub enum DisplayListBuildingResult {
+ NoDisplayListBuildingResult,
+ StackingContextResult(Arc<StackingContext>),
+ DisplayListResult(Box<DisplayList>),
+}
+
+impl DisplayListBuildingResult {
+ /// Adds the display list items contained within this display list building result to the given
+ /// display list, preserving stacking order. If this display list building result does not
+ /// consist of an entire stacking context, it will be emptied.
+ pub fn add_to(&mut self, display_list: &mut DisplayList) {
+ match *self {
+ NoDisplayListBuildingResult => return,
+ StackingContextResult(ref mut stacking_context) => {
+ display_list.children.push_back((*stacking_context).clone())
+ }
+ DisplayListResult(ref mut source_display_list) => {
+ display_list.append_from(&mut **source_display_list)
+ }
+ }
+ }
+}
+
pub trait FragmentDisplayListBuilding {
/// Adds the display items necessary to paint the background of this fragment to the display
/// list if necessary.
fn build_display_list_for_background_if_applicable(&self,
style: &ComputedValues,
- list: &mut DisplayList,
+ display_list: &mut DisplayList,
layout_context: &LayoutContext,
level: StackingLevel,
absolute_bounds: &Rect<Au>,
@@ -65,7 +84,7 @@ pub trait FragmentDisplayListBuilding {
/// display list at the appropriate stacking level.
fn build_display_list_for_background_image(&self,
style: &ComputedValues,
- list: &mut DisplayList,
+ display_list: &mut DisplayList,
layout_context: &LayoutContext,
level: StackingLevel,
absolute_bounds: &Rect<Au>,
@@ -75,7 +94,7 @@ pub trait FragmentDisplayListBuilding {
/// Adds the display items necessary to paint the background linear gradient of this fragment
/// to the display list at the appropriate stacking level.
fn build_display_list_for_background_linear_gradient(&self,
- list: &mut DisplayList,
+ display_list: &mut DisplayList,
level: StackingLevel,
absolute_bounds: &Rect<Au>,
clip_rect: &Rect<Au>,
@@ -86,7 +105,7 @@ pub trait FragmentDisplayListBuilding {
/// necessary.
fn build_display_list_for_borders_if_applicable(&self,
style: &ComputedValues,
- list: &mut DisplayList,
+ display_list: &mut DisplayList,
abs_bounds: &Rect<Au>,
level: StackingLevel,
clip_rect: &Rect<Au>);
@@ -102,11 +121,11 @@ pub trait FragmentDisplayListBuilding {
flow_origin: Point2D<Au>,
clip_rect: &Rect<Au>);
- /// Adds the display items for this fragment to the given stacking context.
+ /// Adds the display items for this fragment to the given display list.
///
/// Arguments:
///
- /// * `display_list`: The unflattened display list to add display items to.
+ /// * `display_list`: The display list to add display items to.
/// * `layout_context`: The layout context.
/// * `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.
@@ -132,7 +151,7 @@ pub trait FragmentDisplayListBuilding {
impl FragmentDisplayListBuilding for Fragment {
fn build_display_list_for_background_if_applicable(&self,
style: &ComputedValues,
- list: &mut DisplayList,
+ display_list: &mut DisplayList,
layout_context: &LayoutContext,
level: StackingLevel,
absolute_bounds: &Rect<Au>,
@@ -143,10 +162,10 @@ impl FragmentDisplayListBuilding for Fragment {
// doesn't have a fragment".
let background_color = style.resolve_color(style.get_background().background_color);
if !background_color.alpha.approx_eq(&0.0) {
- list.push(SolidColorDisplayItemClass(box SolidColorDisplayItem {
- base: BaseDisplayItem::new(*absolute_bounds, self.node, level, *clip_rect),
+ display_list.push(SolidColorDisplayItemClass(box SolidColorDisplayItem {
+ base: BaseDisplayItem::new(*absolute_bounds, self.node, *clip_rect),
color: background_color.to_gfx_color(),
- }));
+ }), level);
}
// The background image is painted on top of the background color.
@@ -156,7 +175,7 @@ impl FragmentDisplayListBuilding for Fragment {
match background.background_image {
None => {}
Some(LinearGradientImage(ref gradient)) => {
- self.build_display_list_for_background_linear_gradient(list,
+ self.build_display_list_for_background_linear_gradient(display_list,
level,
absolute_bounds,
clip_rect,
@@ -165,7 +184,7 @@ impl FragmentDisplayListBuilding for Fragment {
}
Some(UrlImage(ref image_url)) => {
self.build_display_list_for_background_image(style,
- list,
+ display_list,
layout_context,
level,
absolute_bounds,
@@ -177,7 +196,7 @@ impl FragmentDisplayListBuilding for Fragment {
fn build_display_list_for_background_image(&self,
style: &ComputedValues,
- list: &mut DisplayList,
+ display_list: &mut DisplayList,
layout_context: &LayoutContext,
level: StackingLevel,
absolute_bounds: &Rect<Au>,
@@ -255,16 +274,16 @@ impl FragmentDisplayListBuilding for Fragment {
};
// Create the image display item.
- list.push(ImageDisplayItemClass(box ImageDisplayItem {
- base: BaseDisplayItem::new(bounds, self.node, level, clip_rect),
+ display_list.push(ImageDisplayItemClass(box ImageDisplayItem {
+ base: BaseDisplayItem::new(bounds, self.node, clip_rect),
image: image.clone(),
stretch_size: Size2D(Au::from_px(image.width as int),
Au::from_px(image.height as int)),
- }));
+ }), level);
}
fn build_display_list_for_background_linear_gradient(&self,
- list: &mut DisplayList,
+ display_list: &mut DisplayList,
level: StackingLevel,
absolute_bounds: &Rect<Au>,
clip_rect: &Rect<Au>,
@@ -364,18 +383,18 @@ impl FragmentDisplayListBuilding for Fragment {
absolute_bounds.origin.y + absolute_bounds.size.height / 2);
let gradient_display_item = GradientDisplayItemClass(box GradientDisplayItem {
- base: BaseDisplayItem::new(*absolute_bounds, self.node, level, clip_rect),
+ base: BaseDisplayItem::new(*absolute_bounds, self.node, clip_rect),
start_point: center - delta,
end_point: center + delta,
stops: stops,
});
- list.push(gradient_display_item)
+ display_list.push(gradient_display_item, level)
}
fn build_display_list_for_borders_if_applicable(&self,
style: &ComputedValues,
- list: &mut DisplayList,
+ display_list: &mut DisplayList,
abs_bounds: &Rect<Au>,
level: StackingLevel,
clip_rect: &Rect<Au>) {
@@ -390,8 +409,8 @@ impl FragmentDisplayListBuilding for Fragment {
let left_color = style.resolve_color(style.get_border().border_left_color);
// Append the border to the display list.
- list.push(BorderDisplayItemClass(box BorderDisplayItem {
- base: BaseDisplayItem::new(*abs_bounds, self.node, level, *clip_rect),
+ display_list.push(BorderDisplayItemClass(box BorderDisplayItem {
+ base: BaseDisplayItem::new(*abs_bounds, self.node, *clip_rect),
border: border.to_physical(style.writing_mode),
color: SideOffsets2D::new(top_color.to_gfx_color(),
right_color.to_gfx_color(),
@@ -401,7 +420,7 @@ impl FragmentDisplayListBuilding for Fragment {
style.get_border().border_right_style,
style.get_border().border_bottom_style,
style.get_border().border_left_style)
- }));
+ }), level);
}
fn build_debug_borders_around_text_fragments(&self,
@@ -418,11 +437,8 @@ impl FragmentDisplayListBuilding for Fragment {
fragment_bounds.size);
// Compute the text fragment bounds and draw a border surrounding them.
- display_list.push(BorderDisplayItemClass(box BorderDisplayItem {
- base: BaseDisplayItem::new(absolute_fragment_bounds,
- self.node,
- ContentStackingLevel,
- *clip_rect),
+ display_list.content.push_back(BorderDisplayItemClass(box BorderDisplayItem {
+ base: BaseDisplayItem::new(absolute_fragment_bounds, self.node, *clip_rect),
border: SideOffsets2D::new_all_same(Au::from_px(1)),
color: SideOffsets2D::new_all_same(color::rgb(0, 0, 200)),
style: SideOffsets2D::new_all_same(border_style::solid)
@@ -437,11 +453,11 @@ impl FragmentDisplayListBuilding for Fragment {
baseline.origin = baseline.origin + flow_origin;
let line_display_item = box LineDisplayItem {
- base: BaseDisplayItem::new(baseline, self.node, ContentStackingLevel, *clip_rect),
+ base: BaseDisplayItem::new(baseline, self.node, *clip_rect),
color: color::rgb(0, 200, 0),
style: border_style::dashed,
};
- display_list.push(LineDisplayItemClass(line_display_item));
+ display_list.content.push_back(LineDisplayItemClass(line_display_item));
}
fn build_debug_borders_around_fragment(&self,
@@ -457,11 +473,8 @@ impl FragmentDisplayListBuilding for Fragment {
fragment_bounds.size);
// This prints a debug border around the border of this fragment.
- display_list.push(BorderDisplayItemClass(box BorderDisplayItem {
- base: BaseDisplayItem::new(absolute_fragment_bounds,
- self.node,
- ContentStackingLevel,
- *clip_rect),
+ display_list.content.push_back(BorderDisplayItemClass(box BorderDisplayItem {
+ base: BaseDisplayItem::new(absolute_fragment_bounds, self.node, *clip_rect),
border: SideOffsets2D::new_all_same(Au::from_px(1)),
color: SideOffsets2D::new_all_same(color::rgb(0, 0, 200)),
style: SideOffsets2D::new_all_same(border_style::solid)
@@ -474,14 +487,24 @@ impl FragmentDisplayListBuilding for Fragment {
flow_origin: Point2D<Au>,
background_and_border_level: BackgroundAndBorderLevel,
clip_rect: &Rect<Au>) {
+ // Compute the fragment position relative to the parent stacking context. If the fragment
+ // itself establishes a stacking context, then the origin of its position will be (0, 0)
+ // for the purposes of this computation.
+ let stacking_relative_flow_origin = if self.establishes_stacking_context() {
+ ZERO_POINT
+ } else {
+ flow_origin
+ };
+ let absolute_fragment_bounds =
+ self.stacking_relative_bounds(&stacking_relative_flow_origin);
+
// FIXME(#2795): Get the real container size
let container_size = Size2D::zero();
let rect_to_absolute = |writing_mode: WritingMode, logical_rect: LogicalRect<Au>| {
let physical_rect = logical_rect.to_physical(writing_mode, container_size);
- Rect(physical_rect.origin + flow_origin, physical_rect.size)
+ Rect(physical_rect.origin + stacking_relative_flow_origin, physical_rect.size)
};
- // Fragment position wrt to the owning flow.
- let absolute_fragment_bounds = self.abs_bounds_from_origin(&flow_origin);
+
debug!("Fragment::build_display_list at rel={}, abs={}: {}",
self.border_box,
absolute_fragment_bounds,
@@ -512,9 +535,8 @@ impl FragmentDisplayListBuilding for Fragment {
// Add a pseudo-display item for content box queries. This is a very bogus thing to do.
let base_display_item = box BaseDisplayItem::new(absolute_fragment_bounds,
self.node,
- level,
*clip_rect);
- display_list.push(PseudoDisplayItemClass(base_display_item));
+ display_list.push(PseudoDisplayItemClass(base_display_item), level);
// Add the background to the list, if applicable.
match self.inline_context {
@@ -593,17 +615,15 @@ impl FragmentDisplayListBuilding for Fragment {
};
let metrics = &text_fragment.run.font_metrics;
- let baseline_origin ={
- let mut tmp = content_box.start;
- tmp.b = tmp.b + metrics.ascent;
- tmp.to_physical(self.style.writing_mode, container_size) + flow_origin
+ let baseline_origin = {
+ let mut content_box_start = content_box.start;
+ content_box_start.b = content_box_start.b + metrics.ascent;
+ content_box_start.to_physical(self.style.writing_mode, container_size)
+ + flow_origin
};
- display_list.push(TextDisplayItemClass(box TextDisplayItem {
- base: BaseDisplayItem::new(absolute_content_box,
- self.node,
- ContentStackingLevel,
- *clip_rect),
+ display_list.content.push_back(TextDisplayItemClass(box TextDisplayItem {
+ base: BaseDisplayItem::new(absolute_content_box, self.node, *clip_rect),
text_run: text_fragment.run.clone(),
range: text_fragment.range,
text_color: self.style().get_color().color.to_gfx_color(),
@@ -615,19 +635,14 @@ impl FragmentDisplayListBuilding for Fragment {
{
let line = |maybe_color: Option<RGBA>, rect: || -> LogicalRect<Au>| {
match maybe_color {
- None => {},
+ None => {}
Some(color) => {
- display_list.push(SolidColorDisplayItemClass(
- box SolidColorDisplayItem {
- base: BaseDisplayItem::new(
- rect_to_absolute(
- self.style.writing_mode,
- rect()),
- self.node,
- ContentStackingLevel,
- *clip_rect),
- color: color.to_gfx_color(),
- }));
+ let bounds = rect_to_absolute(self.style.writing_mode, rect());
+ display_list.content.push_back(SolidColorDisplayItemClass(
+ box SolidColorDisplayItem {
+ base: BaseDisplayItem::new(bounds, self.node, *clip_rect),
+ color: color.to_gfx_color(),
+ }))
}
}
};
@@ -678,10 +693,9 @@ impl FragmentDisplayListBuilding for Fragment {
debug!("(building display list) building image fragment");
// Place the image into the display list.
- display_list.push(ImageDisplayItemClass(box ImageDisplayItem {
+ display_list.content.push_back(ImageDisplayItemClass(box ImageDisplayItem {
base: BaseDisplayItem::new(absolute_content_box,
self.node,
- ContentStackingLevel,
*clip_rect),
image: image.clone(),
stretch_size: absolute_content_box.size,
@@ -698,7 +712,9 @@ impl FragmentDisplayListBuilding for Fragment {
}
if opts::get().show_debug_fragment_borders {
- self.build_debug_borders_around_fragment(display_list, flow_origin, clip_rect)
+ self.build_debug_borders_around_fragment(display_list,
+ flow_origin,
+ clip_rect)
}
// If this is an iframe, then send its position and size up to the constellation.
@@ -766,6 +782,10 @@ impl FragmentDisplayListBuilding for Fragment {
}
pub trait BlockFlowDisplayListBuilding {
+ fn build_display_list_for_block_base(&mut self,
+ display_list: &mut DisplayList,
+ layout_context: &LayoutContext,
+ background_border_level: BackgroundAndBorderLevel);
fn build_display_list_for_block(&mut self,
layout_context: &LayoutContext,
background_border_level: BackgroundAndBorderLevel);
@@ -775,78 +795,93 @@ pub trait BlockFlowDisplayListBuilding {
}
impl BlockFlowDisplayListBuilding for BlockFlow {
- fn build_display_list_for_block(&mut self,
- layout_context: &LayoutContext,
- background_border_level: BackgroundAndBorderLevel) {
-
+ fn build_display_list_for_block_base(&mut self,
+ display_list: &mut DisplayList,
+ layout_context: &LayoutContext,
+ background_border_level: BackgroundAndBorderLevel) {
// Add the box that starts the block context.
- let absolute_fragment_origin = self.base.child_fragment_absolute_position(&self.fragment);
- self.fragment.build_display_list(&mut self.base.display_list,
+ let stacking_relative_fragment_origin =
+ self.base.stacking_relative_position_of_child_fragment(&self.fragment);
+ self.fragment.build_display_list(display_list,
layout_context,
- absolute_fragment_origin,
+ stacking_relative_fragment_origin,
background_border_level,
&self.base.clip_rect);
- self.base.layers = DList::new();
for kid in self.base.children.iter_mut() {
if flow::base(kid).flags.is_absolutely_positioned() {
// All absolute flows will be handled by their containing block.
continue
}
- self.base.display_list.append_from(&mut flow::mut_base(kid).display_list);
- dlist::append_from(&mut self.base.layers, &mut flow::mut_base(kid).layers)
+ flow::mut_base(kid).display_list_building_result.add_to(display_list);
}
// Process absolute descendant links.
for abs_descendant_link in self.base.abs_descendants.iter() {
// TODO(pradeep): Send in our absolute position directly.
- self.base
- .display_list
- .append_from(&mut flow::mut_base(abs_descendant_link).display_list);
- dlist::append_from(&mut self.base.layers,
- &mut flow::mut_base(abs_descendant_link).layers)
+ flow::mut_base(abs_descendant_link).display_list_building_result.add_to(display_list);
}
}
+ fn build_display_list_for_block(&mut self,
+ layout_context: &LayoutContext,
+ background_border_level: BackgroundAndBorderLevel) {
+ let mut display_list = box DisplayList::new();
+ self.build_display_list_for_block_base(&mut *display_list,
+ layout_context,
+ background_border_level);
+ self.base.display_list_building_result = DisplayListResult(display_list);
+ }
+
fn build_display_list_for_absolutely_positioned_block(&mut self,
layout_context: &LayoutContext) {
- self.build_display_list_for_block(layout_context, RootOfStackingContextLevel);
+ let mut display_list = box DisplayList::new();
+ self.build_display_list_for_block_base(&mut *display_list,
+ layout_context,
+ RootOfStackingContextLevel);
+
+ let bounds = Rect(self.base.stacking_relative_position,
+ self.base.overflow.size.to_physical(self.base.writing_mode));
+ let z_index = self.fragment.style().get_box().z_index.number_or_zero();
if !self.base.absolute_position_info.layers_needed_for_positioned_flows &&
!self.base.flags.needs_layer() {
// We didn't need a layer.
- let z_index = self.fragment.style().get_box().z_index.number_or_zero();
- self.base.display_list.flatten(PositionedDescendantStackingLevel(z_index));
+ self.base.display_list_building_result =
+ StackingContextResult(Arc::new(StackingContext::new(display_list,
+ bounds,
+ z_index,
+ None)));
return
}
// If we got here, then we need a new layer.
- let layer_rect = self.base.position.union(&self.base.overflow);
- let size = Size2D(layer_rect.size.inline.to_nearest_px() as uint,
- layer_rect.size.block.to_nearest_px() as uint);
- let origin = Point2D(self.base.abs_position.x.to_nearest_px() as uint,
- self.base.abs_position.y.to_nearest_px() as uint);
-
let scroll_policy = if self.is_fixed() {
FixedPosition
} else {
Scrollable
};
- self.base.display_list.flatten(ContentStackingLevel);
- let new_layer = RenderLayer {
- id: self.layer_id(0),
- display_list: Arc::new(mem::replace(&mut self.base.display_list, DisplayList::new())),
- position: Rect(origin, size),
- background_color: color::rgba(1.0, 1.0, 1.0, 0.0),
- scroll_policy: scroll_policy,
- };
- self.base.layers.push_back(new_layer)
+
+ let transparent = color::rgba(1.0, 1.0, 1.0, 0.0);
+ let stacking_context =
+ Arc::new(StackingContext::new(display_list,
+ bounds,
+ z_index,
+ Some(Arc::new(RenderLayer::new(self.layer_id(0),
+ transparent,
+ scroll_policy)))));
+
+ self.base.display_list_building_result = StackingContextResult(stacking_context)
}
fn build_display_list_for_floating_block(&mut self, layout_context: &LayoutContext) {
- self.build_display_list_for_block(layout_context, RootOfStackingContextLevel);
- self.base.display_list.flatten(FloatStackingLevel)
+ let mut display_list = box DisplayList::new();
+ self.build_display_list_for_block_base(&mut *display_list,
+ layout_context,
+ RootOfStackingContextLevel);
+ display_list.form_float_pseudo_stacking_context();
+ self.base.display_list_building_result = DisplayListResult(display_list);
}
}
@@ -873,3 +908,51 @@ fn position_to_offset(position: LengthOrPercentage, Au(total_length): Au) -> f32
}
}
+/// "Steps" as defined by CSS 2.1 § E.2.
+#[deriving(Clone, PartialEq, Show)]
+pub enum StackingLevel {
+ /// The border and backgrounds for the root of this stacking context: steps 1 and 2.
+ BackgroundAndBordersStackingLevel,
+ /// Borders and backgrounds for block-level descendants: step 4.
+ BlockBackgroundsAndBordersStackingLevel,
+ /// All other content.
+ ContentStackingLevel,
+}
+
+impl StackingLevel {
+ #[inline]
+ pub fn from_background_and_border_level(level: BackgroundAndBorderLevel) -> StackingLevel {
+ match level {
+ RootOfStackingContextLevel => BackgroundAndBordersStackingLevel,
+ BlockLevel => BlockBackgroundsAndBordersStackingLevel,
+ ContentLevel => ContentStackingLevel,
+ }
+ }
+}
+
+/// Which level to place backgrounds and borders in.
+pub enum BackgroundAndBorderLevel {
+ RootOfStackingContextLevel,
+ BlockLevel,
+ ContentLevel,
+}
+
+trait StackingContextConstruction {
+ /// Adds the given display item at the specified level to this display list.
+ fn push(&mut self, display_item: DisplayItem, level: StackingLevel);
+}
+
+impl StackingContextConstruction for DisplayList {
+ fn push(&mut self, display_item: DisplayItem, level: StackingLevel) {
+ match level {
+ BackgroundAndBordersStackingLevel => {
+ self.background_and_borders.push_back(display_item)
+ }
+ BlockBackgroundsAndBordersStackingLevel => {
+ self.block_backgrounds_and_borders.push_back(display_item)
+ }
+ ContentStackingLevel => self.content.push_back(display_item),
+ }
+ }
+}
+
diff --git a/components/layout/flow.rs b/components/layout/flow.rs
index e22ea27c36e..41cc5570a60 100644
--- a/components/layout/flow.rs
+++ b/components/layout/flow.rs
@@ -28,6 +28,8 @@
use css::node_style::StyledNode;
use block::BlockFlow;
use context::LayoutContext;
+use display_list_builder::{DisplayListBuildingResult, DisplayListResult};
+use display_list_builder::{NoDisplayListBuildingResult, StackingContextResult};
use floats::Floats;
use flow_list::{FlowList, FlowListIterator, MutFlowListIterator};
use flow_ref::FlowRef;
@@ -45,10 +47,7 @@ use table_rowgroup::TableRowGroupFlow;
use table_wrapper::TableWrapperFlow;
use wrapper::ThreadSafeLayoutNode;
-use collections::dlist::DList;
use geom::{Point2D, Rect, Size2D};
-use gfx::display_list::DisplayList;
-use gfx::render_task::RenderLayer;
use serialize::{Encoder, Encodable};
use servo_msg::compositor_msg::LayerId;
use servo_util::geometry::Au;
@@ -681,8 +680,10 @@ pub struct AbsolutePositionInfo {
/// The size of the containing block for relatively-positioned descendants.
pub relative_containing_block_size: LogicalSize<Au>,
- /// The position of the absolute containing block.
- pub absolute_containing_block_position: Point2D<Au>,
+ /// The position of the absolute containing block relative to the nearest ancestor stacking
+ /// context. If the absolute containing block establishes the stacking context for this flow,
+ /// and this flow is not itself absolutely-positioned, then this is (0, 0).
+ pub stacking_relative_position_of_absolute_containing_block: Point2D<Au>,
/// Whether the absolute containing block forces positioned descendants to be layerized.
///
@@ -696,7 +697,7 @@ impl AbsolutePositionInfo {
// of the root layer.
AbsolutePositionInfo {
relative_containing_block_size: LogicalSize::zero(writing_mode),
- absolute_containing_block_position: Zero::zero(),
+ stacking_relative_position_of_absolute_containing_block: Zero::zero(),
layers_needed_for_positioned_flows: false,
}
}
@@ -742,8 +743,9 @@ pub struct BaseFlow {
/// The collapsible margins for this flow, if any.
pub collapsible_margins: CollapsibleMargins,
- /// The position of this flow in page coordinates, computed during display list construction.
- pub abs_position: Point2D<Au>,
+ /// The position of this flow relative to the start of the nearest ancestor stacking context.
+ /// This is computed during the top-down pass of display list construction.
+ pub stacking_relative_position: Point2D<Au>,
/// 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.
@@ -779,11 +781,8 @@ pub struct BaseFlow {
/// rectangles.
pub clip_rect: Rect<Au>,
- /// The unflattened display items for this flow.
- pub display_list: DisplayList,
-
- /// Any layers that we're bubbling up, in a linked list.
- pub layers: DList<RenderLayer>,
+ /// The results of display list building for this flow.
+ pub display_list_building_result: DisplayListBuildingResult,
/// The writing mode for this flow.
pub writing_mode: WritingMode,
@@ -806,8 +805,12 @@ impl<E, S: Encoder<E>> Encodable<S, E> for BaseFlow {
fn encode(&self, e: &mut S) -> Result<(), E> {
e.emit_struct("base", 0, |e| {
try!(e.emit_struct_field("id", 0, |e| self.debug_id().encode(e)))
- try!(e.emit_struct_field("abs_position", 1, |e| self.abs_position.encode(e)))
- try!(e.emit_struct_field("intrinsic_inline_sizes", 2, |e| self.intrinsic_inline_sizes.encode(e)))
+ try!(e.emit_struct_field("stacking_relative_position",
+ 1,
+ |e| self.stacking_relative_position.encode(e)))
+ try!(e.emit_struct_field("intrinsic_inline_sizes",
+ 2,
+ |e| self.intrinsic_inline_sizes.encode(e)))
try!(e.emit_struct_field("position", 3, |e| self.position.encode(e)))
e.emit_struct_field("children", 4, |e| {
e.emit_seq(self.children.len(), |e| {
@@ -893,15 +896,14 @@ impl BaseFlow {
parallel: FlowParallelInfo::new(),
floats: Floats::new(writing_mode),
collapsible_margins: CollapsibleMargins::new(),
- abs_position: Zero::zero(),
+ stacking_relative_position: Zero::zero(),
abs_descendants: Descendants::new(),
absolute_static_i_offset: Au(0),
fixed_static_i_offset: Au(0),
block_container_inline_size: Au(0),
block_container_explicit_block_size: None,
absolute_cb: ContainingBlockLink::new(),
- display_list: DisplayList::new(),
- layers: DList::new(),
+ display_list_building_result: NoDisplayListBuildingResult,
absolute_position_info: AbsolutePositionInfo::new(writing_mode),
clip_rect: Rect(Zero::zero(), Size2D(Au(0), Au(0))),
flags: flags,
@@ -922,13 +924,23 @@ impl BaseFlow {
p as uint
}
+ /// Ensures that all display list items generated by this flow are within the flow's overflow
+ /// rect. This should only be used for debugging.
pub fn validate_display_list_geometry(&self) {
let position_with_overflow = self.position.union(&self.overflow);
- let bounds = Rect(self.abs_position,
+ let bounds = Rect(self.stacking_relative_position,
Size2D(position_with_overflow.size.inline,
position_with_overflow.size.block));
- for item in self.display_list.iter() {
+ let all_items = match self.display_list_building_result {
+ NoDisplayListBuildingResult => Vec::new(),
+ StackingContextResult(ref stacking_context) => {
+ stacking_context.display_list.all_display_items()
+ }
+ DisplayListResult(ref display_list) => display_list.all_display_items(),
+ };
+
+ for item in all_items.iter() {
let paint_bounds = match item.base().bounds.intersection(&item.base().clip_rect) {
None => continue,
Some(rect) => rect,
@@ -944,12 +956,15 @@ impl BaseFlow {
}
}
- pub fn child_fragment_absolute_position(&self, fragment: &Fragment) -> Point2D<Au> {
+ /// Returns the position of the given fragment relative to the start of the nearest ancestor
+ /// stacking context. The fragment must be a child fragment of this flow.
+ pub fn stacking_relative_position_of_child_fragment(&self, fragment: &Fragment)
+ -> Point2D<Au> {
let relative_offset =
fragment.relative_position(&self
.absolute_position_info
.relative_containing_block_size);
- self.abs_position.add_size(&relative_offset.to_physical(self.writing_mode))
+ self.stacking_relative_position.add_size(&relative_offset.to_physical(self.writing_mode))
}
}
diff --git a/components/layout/fragment.rs b/components/layout/fragment.rs
index d9fd9db4839..def9cd33776 100644
--- a/components/layout/fragment.rs
+++ b/components/layout/fragment.rs
@@ -1481,11 +1481,31 @@ impl Fragment {
self.style = (*new_style).clone()
}
- pub fn abs_bounds_from_origin(&self, fragment_origin: &Point2D<Au>) -> Rect<Au> {
+ /// Given the stacking-context-relative position of the containing flow, returns the boundaries
+ /// of this fragment relative to the parent stacking context.
+ pub fn stacking_relative_bounds(&self, stacking_relative_flow_origin: &Point2D<Au>)
+ -> Rect<Au> {
// FIXME(#2795): Get the real container size
let container_size = Size2D::zero();
- self.border_box.to_physical(self.style.writing_mode, container_size)
- .translate(fragment_origin)
+ self.border_box
+ .to_physical(self.style.writing_mode, container_size)
+ .translate(stacking_relative_flow_origin)
+ }
+
+ /// Returns true if this fragment establishes a new stacking context and false otherwise.
+ pub fn establishes_stacking_context(&self) -> bool {
+ match self.style().get_box().position {
+ position::absolute | position::fixed => {
+ // FIXME(pcwalton): This should only establish a new stacking context when
+ // `z-index` is not `auto`. But this matches what we did before.
+ true
+ }
+ position::relative | position::static_ => {
+ // FIXME(pcwalton): `position: relative` establishes a new stacking context if
+ // `z-index` is not `auto`. But this matches what we did before.
+ false
+ }
+ }
}
}
diff --git a/components/layout/inline.rs b/components/layout/inline.rs
index c6ca65fd799..d1a6b22b992 100644
--- a/components/layout/inline.rs
+++ b/components/layout/inline.rs
@@ -6,7 +6,7 @@
use css::node_style::StyledNode;
use context::LayoutContext;
-use display_list_builder::FragmentDisplayListBuilding;
+use display_list_builder::{ContentLevel, DisplayListResult, FragmentDisplayListBuilding};
use floats::{FloatLeft, Floats, PlacementInfo};
use flow::{BaseFlow, FlowClass, Flow, InlineFlowClass, MutableFlowUtils};
use flow;
@@ -20,7 +20,7 @@ use text;
use collections::{RingBuf};
use geom::{Rect, Size2D};
-use gfx::display_list::ContentLevel;
+use gfx::display_list::DisplayList;
use gfx::font::FontMetrics;
use gfx::font_context::FontContext;
use gfx::text::glyph::CharIndex;
@@ -1136,34 +1136,34 @@ impl Flow for InlineFlow {
fn compute_absolute_position(&mut self) {
for fragment in self.fragments.fragments.iter_mut() {
- let absolute_position = match fragment.specific {
+ let stacking_relative_position = match fragment.specific {
InlineBlockFragment(ref mut info) => {
let block_flow = info.flow_ref.as_block();
// FIXME(#2795): Get the real container size
let container_size = Size2D::zero();
- block_flow.base.abs_position =
- self.base.abs_position +
+ block_flow.base.stacking_relative_position =
+ self.base.stacking_relative_position +
fragment.border_box.start.to_physical(self.base.writing_mode,
container_size);
- block_flow.base.abs_position
+ block_flow.base.stacking_relative_position
}
InlineAbsoluteHypotheticalFragment(ref mut info) => {
let block_flow = info.flow_ref.as_block();
// FIXME(#2795): Get the real container size
let container_size = Size2D::zero();
- block_flow.base.abs_position =
- self.base.abs_position +
+ block_flow.base.stacking_relative_position =
+ self.base.stacking_relative_position +
fragment.border_box.start.to_physical(self.base.writing_mode,
container_size);
- block_flow.base.abs_position
+ block_flow.base.stacking_relative_position
}
_ => continue,
};
let clip_rect = fragment.clip_rect_for_children(self.base.clip_rect,
- absolute_position);
+ stacking_relative_position);
match fragment.specific {
InlineBlockFragment(ref mut info) => {
@@ -1183,11 +1183,11 @@ impl Flow for InlineFlow {
fn build_display_list(&mut self, layout_context: &LayoutContext) {
let size = self.base.position.size.to_physical(self.base.writing_mode);
- if !Rect(self.base.abs_position, size).intersects(&layout_context.shared.dirty) {
- debug!("inline block (abs pos {}, size {}) didn't intersect \
- dirty rect two",
- self.base.abs_position,
- size);
+ if !Rect(self.base.stacking_relative_position, size).intersects(&layout_context.shared
+ .dirty) {
+ debug!("inline block (stacking relative pos {}, size {}) didn't intersect dirty rect",
+ self.base.stacking_relative_position,
+ size);
return
}
@@ -1195,9 +1195,10 @@ impl Flow for InlineFlow {
// not recurse on a line if nothing in it can intersect the dirty region.
debug!("Flow: building display list for {:u} inline fragments", self.fragments.len());
+ let mut display_list = box DisplayList::new();
for fragment in self.fragments.fragments.iter_mut() {
- let fragment_origin = self.base.child_fragment_absolute_position(fragment);
- fragment.build_display_list(&mut self.base.display_list,
+ let fragment_origin = self.base.stacking_relative_position_of_child_fragment(fragment);
+ fragment.build_display_list(&mut *display_list,
layout_context,
fragment_origin,
ContentLevel,
@@ -1205,14 +1206,15 @@ impl Flow for InlineFlow {
match fragment.specific {
InlineBlockFragment(ref mut block_flow) => {
let block_flow = block_flow.flow_ref.deref_mut();
- self.base
- .display_list
- .append_from(&mut flow::mut_base(block_flow).display_list)
+ flow::mut_base(block_flow).display_list_building_result
+ .add_to(&mut *display_list)
}
_ => {}
}
}
+ self.base.display_list_building_result = DisplayListResult(display_list);
+
if opts::get().validate_display_list_geometry {
self.base.validate_display_list_geometry();
}
@@ -1223,8 +1225,9 @@ impl Flow for InlineFlow {
fn iterate_through_fragment_bounds(&self, iterator: &mut FragmentBoundsIterator) {
for fragment in self.fragments.fragments.iter() {
if iterator.should_process(fragment) {
- let fragment_origin = self.base.child_fragment_absolute_position(fragment);
- iterator.process(fragment, fragment.abs_bounds_from_origin(&fragment_origin));
+ let fragment_origin =
+ self.base.stacking_relative_position_of_child_fragment(fragment);
+ iterator.process(fragment, fragment.stacking_relative_bounds(&fragment_origin));
}
}
}
diff --git a/components/layout/layout_task.rs b/components/layout/layout_task.rs
index 15d9daccbc8..8fe2afb35ab 100644
--- a/components/layout/layout_task.rs
+++ b/components/layout/layout_task.rs
@@ -19,15 +19,13 @@ use sequential;
use util::{LayoutDataAccess, LayoutDataWrapper, OpaqueNodeMethods, ToGfxColor};
use wrapper::{LayoutNode, TLayoutNode, ThreadSafeLayoutNode};
-use collections::dlist::DList;
use encoding::EncodingRef;
use encoding::all::UTF_8;
use geom::point::Point2D;
use geom::rect::Rect;
use geom::size::Size2D;
use geom::scale_factor::ScaleFactor;
-use gfx::display_list::{ContentStackingLevel, DisplayItem, DisplayList};
-use gfx::display_list::{OpaqueNode};
+use gfx::display_list::{DisplayList, OpaqueNode, StackingContext};
use gfx::render_task::{RenderInitMsg, RenderChan, RenderLayer};
use gfx::{render_task, color};
use layout_traits;
@@ -51,7 +49,6 @@ use gfx::font_cache_task::{FontCacheTask};
use servo_net::local_image_cache::{ImageResponder, LocalImageCache};
use servo_net::resource_task::{ResourceTask, load_bytes_iter};
use servo_util::geometry::Au;
-use servo_util::geometry;
use servo_util::logical_geometry::LogicalPoint;
use servo_util::opts;
use servo_util::smallvec::{SmallVec, SmallVec1, VecLike};
@@ -79,8 +76,8 @@ pub struct LayoutTaskData {
/// The size of the viewport.
pub screen_size: Size2D<Au>,
- /// A cached display list.
- pub display_list: Option<Arc<DisplayList>>,
+ /// The root stacking context.
+ pub stacking_context: Option<Arc<StackingContext>>,
pub stylist: Box<Stylist>,
@@ -277,7 +274,7 @@ impl LayoutTask {
LayoutTaskData {
local_image_cache: local_image_cache,
screen_size: screen_size,
- display_list: None,
+ stacking_context: None,
stylist: box Stylist::new(device),
parallel_traversal: parallel_traversal,
dirty: Rect::zero(),
@@ -621,7 +618,7 @@ impl LayoutTask {
shared_layout_ctx.dirty =
flow::base(layout_root.deref()).position.to_physical(writing_mode,
rw_data.screen_size);
- flow::mut_base(layout_root.deref_mut()).abs_position =
+ flow::mut_base(layout_root.deref_mut()).stacking_relative_position =
LogicalPoint::zero(writing_mode).to_physical(writing_mode,
rw_data.screen_size);
@@ -643,13 +640,7 @@ impl LayoutTask {
}
}
- debug!("Done building display list. Display List = {}",
- flow::base(layout_root.deref()).display_list);
-
- flow::mut_base(layout_root.deref_mut()).display_list.flatten(ContentStackingLevel);
- let display_list =
- Arc::new(mem::replace(&mut flow::mut_base(layout_root.deref_mut()).display_list,
- DisplayList::new()));
+ debug!("Done building display list.");
// FIXME(pcwalton): This is really ugly and can't handle overflow: scroll. Refactor
// it with extreme prejudice.
@@ -678,31 +669,23 @@ impl LayoutTask {
let root_flow = flow::base(layout_root.deref());
root_flow.position.size.to_physical(root_flow.writing_mode)
};
- let root_size = Size2D(root_size.width.to_nearest_px() as uint,
- root_size.height.to_nearest_px() as uint);
- let render_layer = RenderLayer {
- id: layout_root.layer_id(0),
- display_list: display_list.clone(),
- position: Rect(Point2D(0u, 0u), root_size),
- background_color: color,
- scroll_policy: Scrollable,
- };
-
- rw_data.display_list = Some(display_list);
-
- // TODO(pcwalton): Eventually, when we have incremental reflow, this will have to
- // be smarter in order to handle retained layer contents properly from reflow to
- // reflow.
- let mut layers = SmallVec1::new();
- layers.push(render_layer);
- for layer in mem::replace(&mut flow::mut_base(layout_root.deref_mut()).layers,
- DList::new()).into_iter() {
- layers.push(layer)
- }
+ let mut display_list = box DisplayList::new();
+ flow::mut_base(layout_root.deref_mut()).display_list_building_result
+ .add_to(&mut *display_list);
+ let render_layer = Arc::new(RenderLayer::new(layout_root.layer_id(0),
+ color,
+ Scrollable));
+ let origin = Rect(Point2D(Au(0), Au(0)), root_size);
+ let stacking_context = Arc::new(StackingContext::new(display_list,
+ origin,
+ 0,
+ Some(render_layer)));
+
+ rw_data.stacking_context = Some(stacking_context.clone());
debug!("Layout done!");
- self.render_chan.send(RenderInitMsg(layers));
+ self.render_chan.send(RenderInitMsg(stacking_context));
});
}
@@ -933,27 +916,23 @@ impl LayoutRPC for LayoutRPCImpl {
ContentBoxesResponse(rw_data.content_boxes_response.clone())
}
- /// Requests the node containing the point of interest
+ /// Requests the node containing the point of interest.
fn hit_test(&self, _: TrustedNodeAddress, point: Point2D<f32>) -> Result<HitTestResponse, ()> {
- fn hit_test<'a,I>(point: Point2D<Au>, mut iterator: I)
- -> Option<HitTestResponse>
- where I: Iterator<&'a DisplayItem> {
- for item in iterator {
- // TODO(tikue): This check should really be performed by a method of `DisplayItem`.
- if geometry::rect_contains_point(item.base().clip_rect, point) &&
- geometry::rect_contains_point(item.bounds(), point) {
- return Some(HitTestResponse(item.base().node.to_untrusted_node_address()))
- }
- }
- None
- }
let point = Point2D(Au::from_frac_px(point.x as f64), Au::from_frac_px(point.y as f64));
let resp = {
let &LayoutRPCImpl(ref rw_data) = self;
let rw_data = rw_data.lock();
- match rw_data.display_list {
- None => panic!("no display list!"),
- Some(ref display_list) => hit_test(point, display_list.list.iter().rev()),
+ match rw_data.stacking_context {
+ None => panic!("no root stacking context!"),
+ Some(ref stacking_context) => {
+ let mut result = Vec::new();
+ stacking_context.hit_test(point, &mut result, true);
+ if !result.is_empty() {
+ Some(HitTestResponse(result[0]))
+ } else {
+ None
+ }
+ }
}
};
@@ -965,29 +944,17 @@ impl LayoutRPC for LayoutRPCImpl {
fn mouse_over(&self, _: TrustedNodeAddress, point: Point2D<f32>)
-> Result<MouseOverResponse, ()> {
- fn mouse_over_test<'a,I>(point: Point2D<Au>,
- mut iterator: I,
- result: &mut Vec<UntrustedNodeAddress>)
- where I: Iterator<&'a DisplayItem> {
- for item in iterator {
- // TODO(tikue): This check should really be performed by a method of `DisplayItem`.
- if geometry::rect_contains_point(item.bounds(), point) {
- result.push(item.base().node.to_untrusted_node_address())
- }
- }
- }
-
let mut mouse_over_list: Vec<UntrustedNodeAddress> = vec!();
let point = Point2D(Au::from_frac_px(point.x as f64), Au::from_frac_px(point.y as f64));
{
let &LayoutRPCImpl(ref rw_data) = self;
let rw_data = rw_data.lock();
- match rw_data.display_list {
- None => panic!("no display list!"),
- Some(ref display_list) => {
- mouse_over_test(point, display_list.list.iter().rev(), &mut mouse_over_list);
+ match rw_data.stacking_context {
+ None => panic!("no root stacking context!"),
+ Some(ref stacking_context) => {
+ stacking_context.hit_test(point, &mut mouse_over_list, false);
}
- };
+ }
}
if mouse_over_list.is_empty() {
diff --git a/components/util/dlist.rs b/components/util/dlist.rs
index 52836786de3..f22c07268ba 100644
--- a/components/util/dlist.rs
+++ b/components/util/dlist.rs
@@ -96,3 +96,30 @@ pub fn append_from<T>(this: &mut DList<T>, other: &mut DList<T>) {
other.length = 0;
}
}
+
+/// Prepends the items in the other list to this one, leaving the other list empty.
+#[inline]
+pub fn prepend_from<T>(this: &mut DList<T>, other: &mut DList<T>) {
+ unsafe {
+ let this = mem::transmute::<&mut DList<T>,&mut RawDList<T>>(this);
+ let other = mem::transmute::<&mut DList<T>,&mut RawDList<T>>(other);
+ if this.length == 0 {
+ this.head = mem::replace(&mut other.head, ptr::null_mut());
+ this.tail = mem::replace(&mut other.tail, ptr::null_mut());
+ this.length = mem::replace(&mut other.length, 0);
+ return
+ }
+
+ let old_other_tail = mem::replace(&mut other.tail, ptr::null_mut());
+ if old_other_tail.is_null() {
+ return
+ }
+ (*old_other_tail).next = this.head;
+ (*this.head).prev = old_other_tail;
+
+ this.head = mem::replace(&mut other.head, ptr::null_mut());
+ this.length += other.length;
+ other.length = 0;
+ }
+}
+
diff --git a/components/util/geometry.rs b/components/util/geometry.rs
index 8dd6d633da7..06b74c7c3a0 100644
--- a/components/util/geometry.rs
+++ b/components/util/geometry.rs
@@ -74,6 +74,11 @@ impl Default for Au {
}
}
+pub static ZERO_POINT: Point2D<Au> = Point2D {
+ x: Au(0),
+ y: Au(0),
+};
+
pub static ZERO_RECT: Rect<Au> = Rect {
origin: Point2D {
x: Au(0),