servo: Implement stacking contexts and allow multiple layers per

pipeline. This handles fixed positioning mostly correctly.
This commit is contained in:
Patrick Walton 2014-03-28 12:55:29 -07:00
parent f8e3e50db5
commit cd9d824c21
20 changed files with 1726 additions and 1142 deletions

View file

@ -18,64 +18,145 @@ 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 servo_util::smallvec::{SmallVec, SmallVec0, SmallVecIterator};
use std::cast::transmute_region;
use std::cast;
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 {
unsafe {
cast::transmute_copy(self)
}
}
}
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)>,
}
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 iter<'a>(&'a self) -> DisplayListIterator<'a,E> {
ParentDisplayListIterator(self.lists.iter())
}
pub fn add_list(&mut self, list: DisplayList<E>) {
self.lists.push(list);
}
pub fn draw_lists_into_context(&self, render_context: &mut RenderContext) {
for list in self.lists.iter() {
list.draw_into_context(render_context);
}
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;
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,
}
}
/// 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()))
}
}
// 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 +164,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 +179,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 +271,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 +295,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 +327,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 +394,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,7 +427,7 @@ impl<E> DisplayItem<E> {
}
}
pub fn base<'a>(&'a self) -> &'a BaseDisplayItem<E> {
pub fn base<'a>(&'a self) -> &'a BaseDisplayItem {
// FIXME(tkuehn): Workaround for Rust region bug.
unsafe {
match *self {
@ -342,7 +445,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(..) |