mirror of
https://github.com/servo/servo.git
synced 2025-10-15 16:00:28 +01:00
servo: Implement stacking contexts and allow multiple layers per
pipeline. This handles fixed positioning mostly correctly.
This commit is contained in:
parent
f8e3e50db5
commit
cd9d824c21
20 changed files with 1726 additions and 1142 deletions
|
@ -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(..) |
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue