auto merge of #1988 : pcwalton/servo/acid2-2, r=SimonSapin

r? @metajack @SimonSapin @larsbergstrom @june0cho @hyunjunekim
This commit is contained in:
bors-servo 2014-04-03 21:01:48 -04:00
commit 3ec22157ca
62 changed files with 4636 additions and 2791 deletions

View file

@ -18,64 +18,142 @@ use color::Color;
use render_context::RenderContext; use render_context::RenderContext;
use text::TextRun; use text::TextRun;
use geom::{Point2D, Rect, Size2D, SideOffsets2D}; use geom::{Point2D, Rect, SideOffsets2D, Size2D};
use servo_net::image::base::Image; use servo_net::image::base::Image;
use servo_util::geometry::Au; use servo_util::geometry::Au;
use servo_util::range::Range; use servo_util::range::Range;
use std::cast::transmute_region; use servo_util::smallvec::{SmallVec, SmallVec0, SmallVecIterator};
use std::libc::uintptr_t;
use std::mem;
use std::vec::Items; use std::vec::Items;
use style::computed_values::border_style; use style::computed_values::border_style;
use sync::Arc; use sync::Arc;
pub struct DisplayListCollection<E> { /// An opaque handle to a node. The only safe operation that can be performed on this node is to
lists: ~[DisplayList<E>] /// compare it to another opaque handle or to another node.
///
/// Because the script task's GC does not trace layout, node data cannot be safely stored in layout
/// data structures. Also, layout code tends to be faster when the DOM is not being accessed, for
/// locality reasons. Using `OpaqueNode` enforces this invariant.
#[deriving(Clone, Eq)]
pub struct OpaqueNode(uintptr_t);
impl OpaqueNode {
/// Returns the address of this node, for debugging purposes.
pub fn id(&self) -> uintptr_t {
let OpaqueNode(pointer) = *self;
pointer
}
} }
impl<E> DisplayListCollection<E> { /// A stacking context. See CSS 2.1 § E.2. "Steps" below refer to steps in that section of the
pub fn new() -> DisplayListCollection<E> { /// specification.
DisplayListCollection { ///
lists: ~[] /// 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> { pub fn list_for_background_and_border_level<'a>(
ParentDisplayListIterator(self.lists.iter()) &'a mut self,
} level: BackgroundAndBorderLevel)
-> &'a mut DisplayList {
pub fn add_list(&mut self, list: DisplayList<E>) { match level {
self.lists.push(list); RootOfStackingContextLevel => &mut self.background_and_borders,
} BlockLevel => &mut self.block_backgrounds_and_borders,
ContentLevel => &mut self.content,
pub fn draw_lists_into_context(&self, render_context: &mut RenderContext) {
for list in self.lists.iter() {
list.draw_into_context(render_context);
}
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;
} }
} }
/// 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. /// A list of rendering operations to be performed.
pub struct DisplayList<E> { pub struct DisplayList {
list: ~[DisplayItem<E>] list: SmallVec0<DisplayItem>,
} }
pub enum DisplayListIterator<'a,E> { pub enum DisplayListIterator<'a> {
EmptyDisplayListIterator, 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] #[inline]
fn next(&mut self) -> Option<&'a DisplayList<E>> { fn next(&mut self) -> Option<&'a DisplayList> {
match *self { match *self {
EmptyDisplayListIterator => None, EmptyDisplayListIterator => None,
ParentDisplayListIterator(ref mut subiterator) => subiterator.next(), ParentDisplayListIterator(ref mut subiterator) => subiterator.next(),
@ -83,11 +161,11 @@ impl<'a,E> Iterator<&'a DisplayList<E>> for DisplayListIterator<'a,E> {
} }
} }
impl<E> DisplayList<E> { impl DisplayList {
/// Creates a new display list. /// Creates a new display list.
pub fn new() -> DisplayList<E> { pub fn new() -> DisplayList {
DisplayList { DisplayList {
list: ~[] list: SmallVec0::new(),
} }
} }
@ -98,60 +176,62 @@ impl<E> DisplayList<E> {
} }
/// Appends the given item to the display list. /// Appends the given item to the display list.
pub fn append_item(&mut self, item: DisplayItem<E>) { pub fn push(&mut self, item: DisplayItem) {
// FIXME(Issue #150): crashes
//debug!("Adding display item {:u}: {}", self.len(), item);
self.list.push(item) 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. /// Draws the display list into the given render context.
pub fn draw_into_context(&self, render_context: &mut RenderContext) { pub fn draw_into_context(&self, render_context: &mut RenderContext) {
debug!("Beginning display list."); debug!("Beginning display list.");
for item in self.list.iter() { for item in self.list.iter() {
// FIXME(Issue #150): crashes
//debug!("drawing {}", *item);
item.draw_into_context(render_context) item.draw_into_context(render_context)
} }
debug!("Ending display list."); debug!("Ending display list.");
} }
/// Returns a preorder iterator over the given 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()) ParentDisplayItemIterator(self.list.iter())
} }
} }
/// One drawing command in the list. /// One drawing command in the list.
pub enum DisplayItem<E> { pub enum DisplayItem {
SolidColorDisplayItemClass(~SolidColorDisplayItem<E>), SolidColorDisplayItemClass(~SolidColorDisplayItem),
TextDisplayItemClass(~TextDisplayItem<E>), TextDisplayItemClass(~TextDisplayItem),
ImageDisplayItemClass(~ImageDisplayItem<E>), ImageDisplayItemClass(~ImageDisplayItem),
BorderDisplayItemClass(~BorderDisplayItem<E>), BorderDisplayItemClass(~BorderDisplayItem),
LineDisplayItemClass(~LineDisplayItem<E>), LineDisplayItemClass(~LineDisplayItem),
ClipDisplayItemClass(~ClipDisplayItem<E>) ClipDisplayItemClass(~ClipDisplayItem)
} }
/// Information common to all display items. /// Information common to all display items.
pub struct BaseDisplayItem<E> { pub struct BaseDisplayItem {
/// The boundaries of the display item. /// The boundaries of the display item.
/// ///
/// TODO: Which coordinate system should this use? /// TODO: Which coordinate system should this use?
bounds: Rect<Au>, bounds: Rect<Au>,
/// Extra data: either the originating flow (for hit testing) or nothing (for rendering). /// The originating DOM node.
extra: E, node: OpaqueNode,
} }
/// Renders a solid color. /// Renders a solid color.
pub struct SolidColorDisplayItem<E> { pub struct SolidColorDisplayItem {
base: BaseDisplayItem<E>, base: BaseDisplayItem,
color: Color, color: Color,
} }
/// Renders text. /// Renders text.
pub struct TextDisplayItem<E> { pub struct TextDisplayItem {
/// Fields common to all display items. /// Fields common to all display items.
base: BaseDisplayItem<E>, base: BaseDisplayItem,
/// The text run. /// The text run.
text_run: Arc<~TextRun>, text_run: Arc<~TextRun>,
@ -188,14 +268,19 @@ bitfield!(TextDisplayItemFlags, override_overline, set_override_overline, 0x02)
bitfield!(TextDisplayItemFlags, override_line_through, set_override_line_through, 0x04) bitfield!(TextDisplayItemFlags, override_line_through, set_override_line_through, 0x04)
/// Renders an image. /// Renders an image.
pub struct ImageDisplayItem<E> { pub struct ImageDisplayItem {
base: BaseDisplayItem<E>, base: BaseDisplayItem,
image: Arc<~Image>, 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. /// Renders a border.
pub struct BorderDisplayItem<E> { pub struct BorderDisplayItem {
base: BaseDisplayItem<E>, base: BaseDisplayItem,
/// The border widths /// The border widths
border: SideOffsets2D<Au>, border: SideOffsets2D<Au>,
@ -207,31 +292,31 @@ pub struct BorderDisplayItem<E> {
style: SideOffsets2D<border_style::T> style: SideOffsets2D<border_style::T>
} }
/// Renders a line segment /// Renders a line segment.
pub struct LineDisplayItem<E> { pub struct LineDisplayItem {
base: BaseDisplayItem<E>, base: BaseDisplayItem,
/// The line segment color. /// The line segment color.
color: Color, color: Color,
/// The line segemnt style. /// The line segment style.
style: border_style::T style: border_style::T
} }
pub struct ClipDisplayItem<E> { pub struct ClipDisplayItem {
base: BaseDisplayItem<E>, base: BaseDisplayItem,
child_list: ~[DisplayItem<E>], child_list: SmallVec0<DisplayItem>,
need_clip: bool need_clip: bool
} }
pub enum DisplayItemIterator<'a,E> { pub enum DisplayItemIterator<'a> {
EmptyDisplayItemIterator, 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] #[inline]
fn next(&mut self) -> Option<&'a DisplayItem<E>> { fn next(&mut self) -> Option<&'a DisplayItem> {
match *self { match *self {
EmptyDisplayItemIterator => None, EmptyDisplayItemIterator => None,
ParentDisplayItemIterator(ref mut subiterator) => subiterator.next(), ParentDisplayItemIterator(ref mut subiterator) => subiterator.next(),
@ -239,7 +324,7 @@ impl<'a,E> Iterator<&'a DisplayItem<E>> for DisplayItemIterator<'a,E> {
} }
} }
impl<E> DisplayItem<E> { impl DisplayItem {
/// Renders this display item into the given render context. /// Renders this display item into the given render context.
fn draw_into_context(&self, render_context: &mut RenderContext) { fn draw_into_context(&self, render_context: &mut RenderContext) {
match *self { match *self {
@ -306,7 +391,22 @@ impl<E> DisplayItem<E> {
ImageDisplayItemClass(ref image_item) => { ImageDisplayItemClass(ref image_item) => {
debug!("Drawing image at {:?}.", image_item.base.bounds); 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) => { BorderDisplayItemClass(ref border) => {
@ -324,17 +424,14 @@ 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. match *self {
unsafe { SolidColorDisplayItemClass(ref solid_color) => &solid_color.base,
match *self { TextDisplayItemClass(ref text) => &text.base,
SolidColorDisplayItemClass(ref solid_color) => transmute_region(&solid_color.base), ImageDisplayItemClass(ref image_item) => &image_item.base,
TextDisplayItemClass(ref text) => transmute_region(&text.base), BorderDisplayItemClass(ref border) => &border.base,
ImageDisplayItemClass(ref image_item) => transmute_region(&image_item.base), LineDisplayItemClass(ref line) => &line.base,
BorderDisplayItemClass(ref border) => transmute_region(&border.base), ClipDisplayItemClass(ref clip) => &clip.base,
LineDisplayItemClass(ref line) => transmute_region(&line.base),
ClipDisplayItemClass(ref clip) => transmute_region(&clip.base),
}
} }
} }
@ -342,7 +439,7 @@ impl<E> DisplayItem<E> {
self.base().bounds self.base().bounds
} }
pub fn children<'a>(&'a self) -> DisplayItemIterator<'a,E> { pub fn children<'a>(&'a self) -> DisplayItemIterator<'a> {
match *self { match *self {
ClipDisplayItemClass(ref clip) => ParentDisplayItemIterator(clip.child_list.iter()), ClipDisplayItemClass(ref clip) => ParentDisplayItemIterator(clip.child_list.iter()),
SolidColorDisplayItemClass(..) | SolidColorDisplayItemClass(..) |

View file

@ -5,8 +5,8 @@
use font_context::FontContext; use font_context::FontContext;
use style::computed_values::border_style; use style::computed_values::border_style;
use azure::azure_hl::{B8G8R8A8, Color, ColorPattern, DrawOptions}; use azure::azure_hl::{B8G8R8A8, Color, ColorPattern, DrawOptions, DrawSurfaceOptions, DrawTarget};
use azure::azure_hl::{DrawSurfaceOptions, DrawTarget, Linear, StrokeOptions}; use azure::azure_hl::{Linear, SourceOp, StrokeOptions};
use azure::AZ_CAP_BUTT; use azure::AZ_CAP_BUTT;
use azure::AzFloat; use azure::AzFloat;
use geom::point::Point2D; use geom::point::Point2D;
@ -45,7 +45,7 @@ impl<'a> RenderContext<'a> {
pub fn draw_solid_color(&self, bounds: &Rect<Au>, color: Color) { pub fn draw_solid_color(&self, bounds: &Rect<Au>, color: Color) {
self.draw_target.make_current(); self.draw_target.make_current();
self.draw_target.fill_rect(&bounds.to_azure_rect(), &ColorPattern(color)); self.draw_target.fill_rect(&bounds.to_azure_rect(), &ColorPattern(color), None);
} }
pub fn draw_border(&self, pub fn draw_border(&self,
@ -121,13 +121,15 @@ impl<'a> RenderContext<'a> {
} }
pub fn clear(&self) { pub fn clear(&self) {
let pattern = ColorPattern(Color(1.0, 1.0, 1.0, 1.0)); let pattern = ColorPattern(Color(0.0, 0.0, 0.0, 0.0));
let rect = Rect(Point2D(self.page_rect.origin.x as AzFloat, let rect = Rect(Point2D(self.page_rect.origin.x as AzFloat,
self.page_rect.origin.y as AzFloat), self.page_rect.origin.y as AzFloat),
Size2D(self.screen_rect.size.width as AzFloat, Size2D(self.screen_rect.size.width as AzFloat,
self.screen_rect.size.height as AzFloat)); self.screen_rect.size.height as AzFloat));
let mut draw_options = DrawOptions(1.0, 0);
draw_options.set_composition_op(SourceOp);
self.draw_target.make_current(); self.draw_target.make_current();
self.draw_target.fill_rect(&rect, &pattern); self.draw_target.fill_rect(&rect, &pattern, Some(&draw_options));
} }
fn draw_border_segment(&self, direction: Direction, bounds: &Rect<Au>, border: SideOffsets2D<f32>, color: SideOffsets2D<Color>, style: SideOffsets2D<border_style::T>) { fn draw_border_segment(&self, direction: Direction, bounds: &Rect<Au>, border: SideOffsets2D<f32>, color: SideOffsets2D<Color>, style: SideOffsets2D<border_style::T>) {

View file

@ -2,7 +2,12 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// The task that handles all rendering/painting. //! The task that handles all rendering/painting.
use buffer_map::BufferMap;
use display_list::DisplayList;
use font_context::{FontContext, FontContextInfo};
use render_context::RenderContext;
use azure::azure_hl::{B8G8R8A8, Color, DrawTarget, StolenGLResources}; use azure::azure_hl::{B8G8R8A8, Color, DrawTarget, StolenGLResources};
use azure::AzFloat; use azure::AzFloat;
@ -12,12 +17,14 @@ use geom::size::Size2D;
use layers::platform::surface::{NativePaintingGraphicsContext, NativeSurface}; use layers::platform::surface::{NativePaintingGraphicsContext, NativeSurface};
use layers::platform::surface::{NativeSurfaceMethods}; use layers::platform::surface::{NativeSurfaceMethods};
use layers; use layers;
use servo_msg::compositor_msg::{Epoch, IdleRenderState, LayerBuffer, LayerBufferSet}; use servo_msg::compositor_msg::{Epoch, IdleRenderState, LayerBuffer};
use servo_msg::compositor_msg::{RenderListener, RenderingRenderState}; use servo_msg::compositor_msg::{LayerBufferSet, LayerId, LayerMetadata, RenderListener};
use servo_msg::compositor_msg::{RenderingRenderState, ScrollPolicy};
use servo_msg::constellation_msg::{ConstellationChan, PipelineId, RendererReadyMsg}; use servo_msg::constellation_msg::{ConstellationChan, PipelineId, RendererReadyMsg};
use servo_msg::constellation_msg::{Failure, FailureMsg}; use servo_msg::constellation_msg::{Failure, FailureMsg};
use servo_msg::platform::surface::NativeSurfaceAzureMethods; use servo_msg::platform::surface::NativeSurfaceAzureMethods;
use servo_util::opts::Opts; use servo_util::opts::Opts;
use servo_util::smallvec::{SmallVec, SmallVec1};
use servo_util::time::{ProfilerChan, profile}; use servo_util::time::{ProfilerChan, profile};
use servo_util::time; use servo_util::time;
use servo_util::task::send_on_failure; use servo_util::task::send_on_failure;
@ -26,20 +33,23 @@ use std::comm::{Chan, Port};
use std::task; use std::task;
use sync::Arc; use sync::Arc;
use buffer_map::BufferMap; /// Information about a layer that layout sends to the painting task.
use display_list::DisplayListCollection; pub struct RenderLayer {
use font_context::{FontContext, FontContextInfo}; /// A per-pipeline ID describing this layer that should be stable across reflows.
use render_context::RenderContext; id: LayerId,
/// The display list describing the contents of this layer.
pub struct RenderLayer<T> { display_list: Arc<DisplayList>,
display_list_collection: Arc<DisplayListCollection<T>>, /// The position of the layer in pixels.
size: Size2D<uint>, position: Rect<uint>,
color: Color /// The color of the background in this layer. Used for unrendered content.
background_color: Color,
/// The scrolling policy of this layer.
scroll_policy: ScrollPolicy,
} }
pub enum Msg<T> { pub enum Msg {
RenderMsg(RenderLayer<T>), RenderMsg(SmallVec1<RenderLayer>),
ReRenderMsg(~[BufferRequest], f32, Epoch), ReRenderMsg(~[BufferRequest], f32, LayerId, Epoch),
UnusedBufferMsg(~[~LayerBuffer]), UnusedBufferMsg(~[~LayerBuffer]),
PaintPermissionGranted, PaintPermissionGranted,
PaintPermissionRevoked, PaintPermissionRevoked,
@ -63,22 +73,21 @@ pub fn BufferRequest(screen_rect: Rect<uint>, page_rect: Rect<f32>) -> BufferReq
} }
} }
// FIXME(rust#9155): this should be a newtype struct, but // FIXME(#2005, pcwalton): This should be a newtype struct.
// generic newtypes ICE when compiled cross-crate pub struct RenderChan {
pub struct RenderChan<T> { chan: Chan<Msg>,
chan: Chan<Msg<T>>,
} }
impl<T: Send> Clone for RenderChan<T> { impl Clone for RenderChan {
fn clone(&self) -> RenderChan<T> { fn clone(&self) -> RenderChan {
RenderChan { RenderChan {
chan: self.chan.clone(), chan: self.chan.clone(),
} }
} }
} }
impl<T: Send> RenderChan<T> { impl RenderChan {
pub fn new() -> (Port<Msg<T>>, RenderChan<T>) { pub fn new() -> (Port<Msg>, RenderChan) {
let (port, chan) = Chan::new(); let (port, chan) = Chan::new();
let render_chan = RenderChan { let render_chan = RenderChan {
chan: chan, chan: chan,
@ -86,11 +95,11 @@ impl<T: Send> RenderChan<T> {
(port, render_chan) (port, render_chan)
} }
pub fn send(&self, msg: Msg<T>) { pub fn send(&self, msg: Msg) {
assert!(self.try_send(msg), "RenderChan.send: render port closed") assert!(self.try_send(msg), "RenderChan.send: render port closed")
} }
pub fn try_send(&self, msg: Msg<T>) -> bool { pub fn try_send(&self, msg: Msg) -> bool {
self.chan.try_send(msg) self.chan.try_send(msg)
} }
} }
@ -102,9 +111,9 @@ enum GraphicsContext {
GpuGraphicsContext, GpuGraphicsContext,
} }
pub struct RenderTask<C,T> { pub struct RenderTask<C> {
id: PipelineId, id: PipelineId,
port: Port<Msg<T>>, port: Port<Msg>,
compositor: C, compositor: C,
constellation_chan: ConstellationChan, constellation_chan: ConstellationChan,
font_ctx: ~FontContext, font_ctx: ~FontContext,
@ -119,8 +128,8 @@ pub struct RenderTask<C,T> {
/// The native graphics context. /// The native graphics context.
native_graphics_context: Option<NativePaintingGraphicsContext>, native_graphics_context: Option<NativePaintingGraphicsContext>,
/// The layer to be rendered /// The layers to be rendered.
render_layer: Option<RenderLayer<T>>, render_layers: SmallVec1<RenderLayer>,
/// Permission to send paint messages to the compositor /// Permission to send paint messages to the compositor
paint_permission: bool, paint_permission: bool,
@ -140,9 +149,25 @@ macro_rules! native_graphics_context(
) )
) )
impl<C: RenderListener + Send,T:Send+Freeze> RenderTask<C,T> { fn initialize_layers<C:RenderListener>(
compositor: &mut C,
pipeline_id: PipelineId,
epoch: Epoch,
render_layers: &[RenderLayer]) {
let metadata = render_layers.iter().map(|render_layer| {
LayerMetadata {
id: render_layer.id,
position: render_layer.position,
background_color: render_layer.background_color,
scroll_policy: render_layer.scroll_policy,
}
}).collect();
compositor.initialize_layers_for_pipeline(pipeline_id, metadata, epoch);
}
impl<C: RenderListener + Send> RenderTask<C> {
pub fn create(id: PipelineId, pub fn create(id: PipelineId,
port: Port<Msg<T>>, port: Port<Msg>,
compositor: C, compositor: C,
constellation_chan: ConstellationChan, constellation_chan: ConstellationChan,
failure_msg: Failure, failure_msg: Failure,
@ -181,7 +206,7 @@ impl<C: RenderListener + Send,T:Send+Freeze> RenderTask<C,T> {
native_graphics_context: native_graphics_context, native_graphics_context: native_graphics_context,
render_layer: None, render_layers: SmallVec1::new(),
paint_permission: false, paint_permission: false,
epoch: Epoch(0), epoch: Epoch(0),
@ -207,20 +232,25 @@ impl<C: RenderListener + Send,T:Send+Freeze> RenderTask<C,T> {
loop { loop {
match self.port.recv() { match self.port.recv() {
RenderMsg(render_layer) => { RenderMsg(render_layers) => {
if self.paint_permission { self.epoch.next();
self.epoch.next(); self.render_layers = render_layers;
self.compositor.set_layer_page_size_and_color(self.id, render_layer.size, self.epoch, render_layer.color);
} else { if !self.paint_permission {
debug!("render_task: render ready msg"); debug!("render_task: render ready msg");
let ConstellationChan(ref mut c) = self.constellation_chan; let ConstellationChan(ref mut c) = self.constellation_chan;
c.send(RendererReadyMsg(self.id)); c.send(RendererReadyMsg(self.id));
continue;
} }
self.render_layer = Some(render_layer);
initialize_layers(&mut self.compositor,
self.id,
self.epoch,
self.render_layers.as_slice());
} }
ReRenderMsg(tiles, scale, epoch) => { ReRenderMsg(tiles, scale, layer_id, epoch) => {
if self.epoch == epoch { if self.epoch == epoch {
self.render(tiles, scale); self.render(tiles, scale, layer_id);
} else { } else {
debug!("renderer epoch mismatch: {:?} != {:?}", self.epoch, epoch); debug!("renderer epoch mismatch: {:?} != {:?}", self.epoch, epoch);
} }
@ -233,12 +263,16 @@ impl<C: RenderListener + Send,T:Send+Freeze> RenderTask<C,T> {
} }
PaintPermissionGranted => { PaintPermissionGranted => {
self.paint_permission = true; self.paint_permission = true;
match self.render_layer {
Some(ref render_layer) => { // Here we assume that the main layer—the layer responsible for the page size—
self.epoch.next(); // is the first layer. This is a pretty fragile assumption. It will be fixed
self.compositor.set_layer_page_size_and_color(self.id, render_layer.size, self.epoch, render_layer.color); // once we use the layers-based scrolling infrastructure for all scrolling.
} if self.render_layers.len() > 1 {
None => {} self.epoch.next();
initialize_layers(&mut self.compositor,
self.id,
self.epoch,
self.render_layers.as_slice());
} }
} }
PaintPermissionRevoked => { PaintPermissionRevoked => {
@ -253,138 +287,144 @@ impl<C: RenderListener + Send,T:Send+Freeze> RenderTask<C,T> {
} }
} }
fn render(&mut self, tiles: ~[BufferRequest], scale: f32) { /// Renders one layer and sends the tiles back to the layer.
if self.render_layer.is_none() { ///
return /// FIXME(pcwalton): We will probably want to eventually send all layers belonging to a page in
} /// one transaction, to avoid the user seeing inconsistent states.
fn render(&mut self, tiles: ~[BufferRequest], scale: f32, layer_id: LayerId) {
self.compositor.set_render_state(RenderingRenderState);
time::profile(time::RenderingCategory, self.profiler_chan.clone(), || { time::profile(time::RenderingCategory, self.profiler_chan.clone(), || {
// FIXME: Try not to create a new array here. // FIXME: Try not to create a new array here.
let mut new_buffers = ~[]; let mut new_buffers = ~[];
// Divide up the layer into tiles. // Find the appropriate render layer.
time::profile(time::RenderingPrepBuffCategory, self.profiler_chan.clone(), || { let render_layer = match self.render_layers.iter().find(|layer| layer.id == layer_id) {
for tile in tiles.iter() { Some(render_layer) => render_layer,
let width = tile.screen_rect.size.width; None => return,
let height = tile.screen_rect.size.height; };
let size = Size2D(width as i32, height as i32); self.compositor.set_render_state(RenderingRenderState);
let draw_target = match self.graphics_context {
CpuGraphicsContext => { // Divide up the layer into tiles.
DrawTarget::new(self.opts.render_backend, size, B8G8R8A8) for tile in tiles.iter() {
} let width = tile.screen_rect.size.width;
GpuGraphicsContext => { let height = tile.screen_rect.size.height;
// FIXME(pcwalton): Cache the components of draw targets
// (texture color buffer, renderbuffers) instead of recreating them. let size = Size2D(width as i32, height as i32);
let draw_target = let draw_target = match self.graphics_context {
DrawTarget::new_with_fbo(self.opts.render_backend, CpuGraphicsContext => {
native_graphics_context!(self), DrawTarget::new(self.opts.render_backend, size, B8G8R8A8)
size, }
B8G8R8A8); GpuGraphicsContext => {
draw_target.make_current(); // FIXME(pcwalton): Cache the components of draw targets
draw_target // (texture color buffer, renderbuffers) instead of recreating them.
} let draw_target =
DrawTarget::new_with_fbo(self.opts.render_backend,
native_graphics_context!(self),
size,
B8G8R8A8);
draw_target.make_current();
draw_target
}
};
{
// Build the render context.
let mut ctx = RenderContext {
draw_target: &draw_target,
font_ctx: &mut self.font_ctx,
opts: &self.opts,
page_rect: tile.page_rect,
screen_rect: tile.screen_rect,
}; };
{ // Apply the translation to render the tile we want.
// Build the render context. let matrix: Matrix2D<AzFloat> = Matrix2D::identity();
let mut ctx = RenderContext { let matrix = matrix.scale(scale as AzFloat, scale as AzFloat);
draw_target: &draw_target, let matrix = matrix.translate(-(tile.page_rect.origin.x) as AzFloat,
font_ctx: &mut self.font_ctx, -(tile.page_rect.origin.y) as AzFloat);
opts: &self.opts, let matrix = matrix.translate(-(render_layer.position.origin.x as AzFloat),
page_rect: tile.page_rect, -(render_layer.position.origin.y as AzFloat));
screen_rect: tile.screen_rect,
ctx.draw_target.set_transform(&matrix);
// Clear the buffer.
ctx.clear();
// Draw the display list.
profile(time::RenderingDrawingCategory, self.profiler_chan.clone(), || {
render_layer.display_list.get().draw_into_context(&mut ctx);
ctx.draw_target.flush();
});
}
// Extract the texture from the draw target and place it into its slot in the
// buffer. If using CPU rendering, upload it first.
//
// FIXME(pcwalton): We should supply the texture and native surface *to* the
// draw target in GPU rendering mode, so that it doesn't have to recreate it.
let buffer = match self.graphics_context {
CpuGraphicsContext => {
let buffer = match self.buffer_map.find(tile.screen_rect.size) {
Some(buffer) => {
let mut buffer = buffer;
buffer.rect = tile.page_rect;
buffer.screen_pos = tile.screen_rect;
buffer.resolution = scale;
buffer.native_surface.mark_wont_leak();
buffer
}
None => {
// Create an empty native surface. We mark it as not leaking
// in case it dies in transit to the compositor task.
let mut native_surface: NativeSurface =
layers::platform::surface::NativeSurfaceMethods::new(
native_graphics_context!(self),
Size2D(width as i32, height as i32),
width as i32 * 4);
native_surface.mark_wont_leak();
~LayerBuffer {
native_surface: native_surface,
rect: tile.page_rect,
screen_pos: tile.screen_rect,
resolution: scale,
stride: (width * 4) as uint
}
}
}; };
// Apply the translation to render the tile we want. draw_target.snapshot().get_data_surface().with_data(|data| {
let matrix: Matrix2D<AzFloat> = Matrix2D::identity(); buffer.native_surface.upload(native_graphics_context!(self), data);
let matrix = matrix.scale(scale as AzFloat, scale as AzFloat); debug!("RENDERER uploading to native surface {:d}",
let matrix = matrix.translate(-(tile.page_rect.origin.x) as AzFloat, buffer.native_surface.get_id() as int);
-(tile.page_rect.origin.y) as AzFloat);
ctx.draw_target.set_transform(&matrix);
// Clear the buffer.
ctx.clear();
// Draw the display list.
profile(time::RenderingDrawingCategory, self.profiler_chan.clone(), || {
let render_layer = self.render_layer.as_ref().unwrap();
render_layer.display_list_collection.get().draw_lists_into_context(&mut ctx);
ctx.draw_target.flush();
}); });
buffer
} }
GpuGraphicsContext => {
draw_target.make_current();
let StolenGLResources {
surface: native_surface
} = draw_target.steal_gl_resources().unwrap();
// Extract the texture from the draw target and place it into its slot in the // We mark the native surface as not leaking in case the surfaces
// buffer. If using CPU rendering, upload it first. // die on their way to the compositor task.
// let mut native_surface: NativeSurface =
// FIXME(pcwalton): We should supply the texture and native surface *to* the NativeSurfaceAzureMethods::from_azure_surface(native_surface);
// draw target in GPU rendering mode, so that it doesn't have to recreate it. native_surface.mark_wont_leak();
let buffer = match self.graphics_context {
CpuGraphicsContext => {
let buffer = match self.buffer_map.find(tile.screen_rect.size) {
Some(buffer) => {
let mut buffer = buffer;
buffer.rect = tile.page_rect;
buffer.screen_pos = tile.screen_rect;
buffer.resolution = scale;
buffer.native_surface.mark_wont_leak();
buffer
}
None => {
// Create an empty native surface. We mark it as not leaking
// in case it dies in transit to the compositor task.
let mut native_surface: NativeSurface =
layers::platform::surface::NativeSurfaceMethods::new(
native_graphics_context!(self),
Size2D(width as i32, height as i32),
width as i32 * 4);
native_surface.mark_wont_leak();
~LayerBuffer { ~LayerBuffer {
native_surface: native_surface, native_surface: native_surface,
rect: tile.page_rect, rect: tile.page_rect,
screen_pos: tile.screen_rect, screen_pos: tile.screen_rect,
resolution: scale, resolution: scale,
stride: (width * 4) as uint stride: (width * 4) as uint
}
}
};
draw_target.snapshot().get_data_surface().with_data(|data| {
buffer.native_surface.upload(native_graphics_context!(self), data);
debug!("RENDERER uploading to native surface {:d}",
buffer.native_surface.get_id() as int);
});
buffer
} }
GpuGraphicsContext => { }
draw_target.make_current(); };
let StolenGLResources {
surface: native_surface
} = draw_target.steal_gl_resources().unwrap();
// We mark the native surface as not leaking in case the surfaces new_buffers.push(buffer);
// die on their way to the compositor task. }
let mut native_surface: NativeSurface =
NativeSurfaceAzureMethods::from_azure_surface(native_surface);
native_surface.mark_wont_leak();
~LayerBuffer {
native_surface: native_surface,
rect: tile.page_rect,
screen_pos: tile.screen_rect,
resolution: scale,
stride: (width * 4) as uint
}
}
};
new_buffers.push(buffer);
}
});
let layer_buffer_set = ~LayerBufferSet { let layer_buffer_set = ~LayerBufferSet {
buffers: new_buffers, buffers: new_buffers,
@ -392,7 +432,7 @@ impl<C: RenderListener + Send,T:Send+Freeze> RenderTask<C,T> {
debug!("render_task: returning surface"); debug!("render_task: returning surface");
if self.paint_permission { if self.paint_permission {
self.compositor.paint(self.id, layer_buffer_set, self.epoch); self.compositor.paint(self.id, render_layer.id, layer_buffer_set, self.epoch);
} else { } else {
debug!("render_task: RendererReadyMsg send"); debug!("render_task: RendererReadyMsg send");
let ConstellationChan(ref mut c) = self.constellation_chan; let ConstellationChan(ref mut c) = self.constellation_chan;

View file

@ -5,16 +5,13 @@
use constellation::SendableFrameTree; use constellation::SendableFrameTree;
use compositing::compositor_layer::CompositorLayer; use compositing::compositor_layer::CompositorLayer;
use compositing::*; use compositing::*;
use pipeline::CompositionPipeline;
use platform::{Application, Window}; use platform::{Application, Window};
use windowing::{FinishedWindowEvent, IdleWindowEvent, LoadUrlWindowEvent, MouseWindowClickEvent};
use windowing::{WindowEvent, WindowMethods, use windowing::{MouseWindowEvent, MouseWindowEventClass, MouseWindowMouseDownEvent};
WindowNavigateMsg, use windowing::{MouseWindowMouseUpEvent, MouseWindowMoveEventClass, NavigationWindowEvent};
IdleWindowEvent, RefreshWindowEvent, ResizeWindowEvent, LoadUrlWindowEvent, use windowing::{QuitWindowEvent, RefreshWindowEvent, ResizeWindowEvent, ScrollWindowEvent};
MouseWindowEventClass, MouseWindowMoveEventClass,ScrollWindowEvent, ZoomWindowEvent, NavigationWindowEvent, use windowing::{WindowEvent, WindowMethods, WindowNavigateMsg, ZoomWindowEvent};
FinishedWindowEvent, QuitWindowEvent,
MouseWindowEvent, MouseWindowClickEvent, MouseWindowMouseDownEvent, MouseWindowMouseUpEvent};
use azure::azure_hl::{SourceSurfaceMethods, Color}; use azure::azure_hl::{SourceSurfaceMethods, Color};
use azure::azure_hl; use azure::azure_hl;
@ -29,8 +26,10 @@ use layers::rendergl::RenderContext;
use layers::scene::Scene; use layers::scene::Scene;
use opengles::gl2; use opengles::gl2;
use png; use png;
use servo_msg::compositor_msg::{Blank, Epoch, FinishedLoading, IdleRenderState, LayerBufferSet, ReadyState, RenderState}; use servo_msg::compositor_msg::{Blank, Epoch, FinishedLoading, IdleRenderState, LayerBufferSet};
use servo_msg::constellation_msg::{ConstellationChan, ExitMsg, NavigateMsg, ResizedWindowMsg, LoadUrlMsg, PipelineId}; use servo_msg::compositor_msg::{LayerId, ReadyState, RenderState, ScrollPolicy, Scrollable};
use servo_msg::constellation_msg::{ConstellationChan, ExitMsg, LoadUrlMsg, NavigateMsg};
use servo_msg::constellation_msg::{PipelineId, ResizedWindowMsg};
use servo_msg::constellation_msg; use servo_msg::constellation_msg;
use servo_util::opts::Opts; use servo_util::opts::Opts;
use servo_util::time::{profile, ProfilerChan, Timer}; use servo_util::time::{profile, ProfilerChan, Timer};
@ -55,6 +54,9 @@ pub struct IOCompositor {
/// The root ContainerLayer. /// The root ContainerLayer.
root_layer: Rc<ContainerLayer>, root_layer: Rc<ContainerLayer>,
/// The root pipeline.
root_pipeline: Option<CompositionPipeline>,
/// The canvas to paint a page. /// The canvas to paint a page.
scene: Scene, scene: Scene,
@ -110,7 +112,6 @@ pub struct IOCompositor {
} }
impl IOCompositor { impl IOCompositor {
pub fn new(app: &Application, pub fn new(app: &Application,
opts: Opts, opts: Opts,
port: Port<Msg>, port: Port<Msg>,
@ -131,6 +132,7 @@ impl IOCompositor {
opts: opts, opts: opts,
context: rendergl::init_render_context(), context: rendergl::init_render_context(),
root_layer: root_layer.clone(), root_layer: root_layer.clone(),
root_pipeline: None,
scene: Scene(ContainerLayerKind(root_layer), window_size, identity()), scene: Scene(ContainerLayerKind(root_layer), window_size, identity()),
window_size: Size2D(window_size.width as uint, window_size.height as uint), window_size: Size2D(window_size.width as uint, window_size.height as uint),
graphics_context: CompositorTask::create_graphics_context(), graphics_context: CompositorTask::create_graphics_context(),
@ -253,11 +255,10 @@ impl IOCompositor {
self.change_render_state(render_state); self.change_render_state(render_state);
} }
(Data(SetUnRenderedColor(_id, color)), false) => { (Data(SetUnRenderedColor(pipeline_id, layer_id, color)), false) => {
self.set_unrendered_color(_id, color); self.set_unrendered_color(pipeline_id, layer_id, color);
} }
(Data(SetIds(frame_tree, response_chan, new_constellation_chan)), _) => { (Data(SetIds(frame_tree, response_chan, new_constellation_chan)), _) => {
self.set_ids(frame_tree, response_chan, new_constellation_chan); self.set_ids(frame_tree, response_chan, new_constellation_chan);
} }
@ -266,32 +267,44 @@ impl IOCompositor {
chan.send(Some(azure_hl::current_graphics_metadata())); chan.send(Some(azure_hl::current_graphics_metadata()));
} }
(Data(NewLayer(_id, new_size)), false) => { (Data(CreateRootCompositorLayerIfNecessary(pipeline_id, layer_id, size)),
self.create_new_layer(_id, new_size); false) => {
self.create_root_compositor_layer_if_necessary(pipeline_id, layer_id, size);
} }
(Data(SetLayerPageSize(id, new_size, epoch)), false) => { (Data(CreateDescendantCompositorLayerIfNecessary(pipeline_id,
self.set_layer_page_size(id, new_size, epoch); layer_id,
rect,
scroll_behavior)),
false) => {
self.create_descendant_compositor_layer_if_necessary(pipeline_id,
layer_id,
rect,
scroll_behavior);
} }
(Data(SetLayerClipRect(id, new_rect)), false) => { (Data(SetLayerPageSize(pipeline_id, layer_id, new_size, epoch)), false) => {
self.set_layer_clip_rect(id, new_rect); self.set_layer_page_size(pipeline_id, layer_id, new_size, epoch);
} }
(Data(DeleteLayer(id)), _) => { (Data(SetLayerClipRect(pipeline_id, layer_id, new_rect)), false) => {
self.set_layer_clip_rect(pipeline_id, layer_id, new_rect);
}
(Data(DeleteLayerGroup(id)), _) => {
self.delete_layer(id); self.delete_layer(id);
} }
(Data(Paint(id, new_layer_buffer_set, epoch)), false) => { (Data(Paint(pipeline_id, layer_id, new_layer_buffer_set, epoch)), false) => {
self.paint(id, new_layer_buffer_set, epoch); self.paint(pipeline_id, layer_id, new_layer_buffer_set, epoch);
} }
(Data(InvalidateRect(id, rect)), false) => { (Data(InvalidateRect(pipeline_id, layer_id, rect)), false) => {
self.invalidate_rect(id, rect); self.invalidate_rect(pipeline_id, layer_id, rect);
} }
(Data(ScrollFragmentPoint(id, point)), false) => { (Data(ScrollFragmentPoint(pipeline_id, layer_id, point)), false) => {
self.scroll_fragment_to_point(id, point); self.scroll_fragment_to_point(pipeline_id, layer_id, point);
} }
(Data(LoadComplete(..)), false) => { (Data(LoadComplete(..)), false) => {
@ -313,11 +326,10 @@ impl IOCompositor {
} }
} }
fn set_unrendered_color(&mut self, _id: PipelineId, color: Color) { // FIXME(#2004, pcwalton): Take the pipeline ID and layer ID into account.
fn set_unrendered_color(&mut self, _: PipelineId, _: LayerId, color: Color) {
match self.compositor_layer { match self.compositor_layer {
Some(ref mut layer) => { Some(ref mut layer) => layer.unrendered_color = color,
layer.unrendered_color = color;
}
None => {} None => {}
} }
} }
@ -328,31 +340,7 @@ impl IOCompositor {
new_constellation_chan: ConstellationChan) { new_constellation_chan: ConstellationChan) {
response_chan.send(()); response_chan.send(());
// This assumes there is at most one child, which should be the case. self.root_pipeline = Some(frame_tree.pipeline.clone());
// NOTE: work around borrowchk
{
let tmp = self.root_layer.borrow().first_child.borrow();
match *tmp.get() {
Some(ref old_layer) => ContainerLayer::remove_child(self.root_layer.clone(),
old_layer.clone()),
None => {}
}
}
let layer = CompositorLayer::from_frame_tree(frame_tree,
self.opts.tile_size,
Some(10000000u),
self.opts.cpu_painting);
ContainerLayer::add_child_start(self.root_layer.clone(),
ContainerLayerKind(layer.root_layer.clone()));
// If there's already a root layer, destroy it cleanly.
match self.compositor_layer {
None => {}
Some(ref mut compositor_layer) => compositor_layer.clear_all(),
}
self.compositor_layer = Some(layer);
// Initialize the new constellation channel by sending it the root window size. // Initialize the new constellation channel by sending it the root window size.
let window_size = self.window.borrow().size(); let window_size = self.window.borrow().size();
@ -366,39 +354,81 @@ impl IOCompositor {
self.constellation_chan = new_constellation_chan; self.constellation_chan = new_constellation_chan;
} }
fn create_new_layer(&mut self, _id: PipelineId, new_size: Size2D<f32>) { // FIXME(pcwalton): Take the pipeline ID into account.
// FIXME: This should create an additional layer instead of replacing the current one. fn create_root_compositor_layer_if_necessary(&mut self,
// Once ResizeLayer messages are set up, we can switch to the new functionality. _: PipelineId,
layer_id: LayerId,
size: Size2D<f32>) {
let (root_pipeline, root_layer_id) = match self.compositor_layer {
Some(ref compositor_layer) => {
(compositor_layer.pipeline.clone(), compositor_layer.id_of_first_child())
}
None => {
match self.root_pipeline {
Some(ref root_pipeline) => (root_pipeline.clone(), LayerId::null()),
None => fail!("Compositor: Received new layer without initialized pipeline"),
}
}
};
let p = match self.compositor_layer { if layer_id != root_layer_id {
Some(ref compositor_layer) => compositor_layer.pipeline.clone(), let root_pipeline_id = root_pipeline.id;
let mut new_layer = CompositorLayer::new_root(root_pipeline,
size,
self.opts.tile_size,
self.opts.cpu_painting);
{
let current_child = self.root_layer.borrow().first_child.borrow();
match *current_child.get() {
None => {}
Some(ref old_layer) => {
ContainerLayer::remove_child(self.root_layer.clone(), old_layer.clone())
}
}
}
assert!(new_layer.add_child_if_necessary(self.root_layer.clone(),
root_pipeline_id,
new_layer.id,
layer_id,
Rect(Point2D(0f32, 0f32), size),
size,
Scrollable));
ContainerLayer::add_child_start(self.root_layer.clone(),
ContainerLayerKind(new_layer.root_layer.clone()));
self.compositor_layer = Some(new_layer);
}
self.ask_for_tiles();
}
fn create_descendant_compositor_layer_if_necessary(&mut self,
pipeline_id: PipelineId,
layer_id: LayerId,
rect: Rect<f32>,
scroll_policy: ScrollPolicy) {
match self.compositor_layer {
Some(ref mut compositor_layer) => {
assert!(compositor_layer.add_child_if_necessary(self.root_layer.clone(),
pipeline_id,
compositor_layer.id,
layer_id,
rect,
compositor_layer.page_size
.unwrap(),
scroll_policy))
}
None => fail!("Compositor: Received new layer without initialized pipeline"), None => fail!("Compositor: Received new layer without initialized pipeline"),
}; };
let page_size = Size2D(new_size.width as f32, new_size.height as f32);
let new_layer = CompositorLayer::new(p,
Some(page_size),
self.opts.tile_size,
Some(10000000u),
self.opts.cpu_painting);
{
let current_child = self.root_layer.borrow().first_child.borrow();
// This assumes there is at most one child, which should be the case.
match *current_child.get() {
Some(ref old_layer) => ContainerLayer::remove_child(self.root_layer.clone(),
old_layer.clone()),
None => {}
}
}
ContainerLayer::add_child_start(self.root_layer.clone(),
ContainerLayerKind(new_layer.root_layer.clone()));
self.compositor_layer = Some(new_layer);
self.ask_for_tiles(); self.ask_for_tiles();
} }
fn set_layer_page_size(&mut self, fn set_layer_page_size(&mut self,
id: PipelineId, pipeline_id: PipelineId,
layer_id: LayerId,
new_size: Size2D<f32>, new_size: Size2D<f32>,
epoch: Epoch) { epoch: Epoch) {
let (ask, move): (bool, bool) = match self.compositor_layer { let (ask, move): (bool, bool) = match self.compositor_layer {
@ -407,8 +437,10 @@ impl IOCompositor {
let world_zoom = self.world_zoom; let world_zoom = self.world_zoom;
let page_window = Size2D(window_size.width as f32 / world_zoom, let page_window = Size2D(window_size.width as f32 / world_zoom,
window_size.height as f32 / world_zoom); window_size.height as f32 / world_zoom);
layer.resize(id, new_size, page_window, epoch); layer.resize(pipeline_id, layer_id, new_size, page_window, epoch);
let move = self.fragment_point.take().map_or(false, |point| layer.move(point, page_window)); let move = self.fragment_point.take().map_or(false, |point| {
layer.move(pipeline_id, layer_id, point, page_window)
});
(true, move) (true, move)
} }
@ -421,10 +453,13 @@ impl IOCompositor {
} }
} }
fn set_layer_clip_rect(&mut self, id: PipelineId, new_rect: Rect<f32>) { fn set_layer_clip_rect(&mut self,
pipeline_id: PipelineId,
layer_id: LayerId,
new_rect: Rect<f32>) {
let ask: bool = match self.compositor_layer { let ask: bool = match self.compositor_layer {
Some(ref mut layer) => { Some(ref mut layer) => {
assert!(layer.set_clipping_rect(id, new_rect)); assert!(layer.set_clipping_rect(pipeline_id, layer_id, new_rect));
true true
} }
None => { None => {
@ -454,10 +489,11 @@ impl IOCompositor {
} }
fn paint(&mut self, fn paint(&mut self,
id: PipelineId, pipeline_id: PipelineId,
layer_id: LayerId,
new_layer_buffer_set: ~LayerBufferSet, new_layer_buffer_set: ~LayerBufferSet,
epoch: Epoch) { epoch: Epoch) {
debug!("osmain: received new frame"); debug!("compositor received new frame");
// From now on, if we destroy the buffers, they will leak. // From now on, if we destroy the buffers, they will leak.
let mut new_layer_buffer_set = new_layer_buffer_set; let mut new_layer_buffer_set = new_layer_buffer_set;
@ -466,27 +502,29 @@ impl IOCompositor {
match self.compositor_layer { match self.compositor_layer {
Some(ref mut layer) => { Some(ref mut layer) => {
assert!(layer.add_buffers(&self.graphics_context, assert!(layer.add_buffers(&self.graphics_context,
id, pipeline_id,
layer_id,
new_layer_buffer_set, new_layer_buffer_set,
epoch).is_none()); epoch).is_none());
self.recomposite = true; self.recomposite = true;
} }
None => { None => {
fail!("Compositor: given paint command with no CompositorLayer initialized"); fail!("compositor given paint command with no CompositorLayer initialized");
} }
} }
// TODO: Recycle the old buffers; send them back to the renderer to reuse if // TODO: Recycle the old buffers; send them back to the renderer to reuse if
// it wishes. // it wishes.
} }
fn invalidate_rect(&mut self, id: PipelineId, rect: Rect<uint>) { fn invalidate_rect(&mut self, pipeline_id: PipelineId, layer_id: LayerId, rect: Rect<uint>) {
let ask: bool = match self.compositor_layer { let ask: bool = match self.compositor_layer {
Some(ref mut layer) => { Some(ref mut layer) => {
let point = Point2D(rect.origin.x as f32, let point = Point2D(rect.origin.x as f32,
rect.origin.y as f32); rect.origin.y as f32);
let size = Size2D(rect.size.width as f32, let size = Size2D(rect.size.width as f32,
rect.size.height as f32); rect.size.height as f32);
layer.invalidate_rect(id, Rect(point, size)); layer.invalidate_rect(pipeline_id, layer_id, Rect(point, size));
true true
} }
None => { None => {
@ -500,14 +538,18 @@ impl IOCompositor {
} }
} }
fn scroll_fragment_to_point(&mut self, id: PipelineId, point: Point2D<f32>) { fn scroll_fragment_to_point(&mut self,
pipeline_id: PipelineId,
layer_id: LayerId,
point: Point2D<f32>) {
let world_zoom = self.world_zoom; let world_zoom = self.world_zoom;
let page_window = Size2D(self.window_size.width as f32 / world_zoom, let page_window = Size2D(self.window_size.width as f32 / world_zoom,
self.window_size.height as f32 / world_zoom); self.window_size.height as f32 / world_zoom);
let (ask, move): (bool, bool) = match self.compositor_layer {
Some(ref mut layer) if layer.pipeline.id == id && !layer.hidden => {
(true, layer.move(point, page_window)) let (ask, move): (bool, bool) = match self.compositor_layer {
Some(ref mut layer) if layer.pipeline.id == pipeline_id && !layer.hidden => {
(true, layer.move(pipeline_id, layer_id, point, page_window))
} }
Some(_) | None => { Some(_) | None => {
self.fragment_point = Some(point); self.fragment_point = Some(point);
@ -630,7 +672,7 @@ impl IOCompositor {
self.window_size.height as f32 / world_zoom); self.window_size.height as f32 / world_zoom);
let mut scroll = false; let mut scroll = false;
for layer in self.compositor_layer.mut_iter() { for layer in self.compositor_layer.mut_iter() {
scroll = layer.scroll(page_delta, page_cursor, page_window) || scroll; scroll = layer.handle_scroll_event(page_delta, page_cursor, page_window) || scroll;
} }
self.recomposite_if(scroll); self.recomposite_if(scroll);
self.ask_for_tiles(); self.ask_for_tiles();
@ -656,7 +698,7 @@ impl IOCompositor {
let page_window = Size2D(window_size.width as f32 / world_zoom, let page_window = Size2D(window_size.width as f32 / world_zoom,
window_size.height as f32 / world_zoom); window_size.height as f32 / world_zoom);
for layer in self.compositor_layer.mut_iter() { for layer in self.compositor_layer.mut_iter() {
layer.scroll(page_delta, page_cursor, page_window); layer.handle_scroll_event(page_delta, page_cursor, page_window);
} }
self.recomposite = true; self.recomposite = true;
@ -679,7 +721,9 @@ impl IOCompositor {
for layer in self.compositor_layer.mut_iter() { for layer in self.compositor_layer.mut_iter() {
if !layer.hidden { if !layer.hidden {
let rect = Rect(Point2D(0f32, 0f32), window_size_page); let rect = Rect(Point2D(0f32, 0f32), window_size_page);
let recomposite = layer.get_buffer_request(&self.graphics_context, rect, world_zoom) || let recomposite = layer.get_buffer_request(&self.graphics_context,
rect,
world_zoom) ||
self.recomposite; self.recomposite;
self.recomposite = recomposite; self.recomposite = recomposite;
} else { } else {
@ -756,3 +800,4 @@ impl IOCompositor {
self.recomposite = result || self.recomposite; self.recomposite = result || self.recomposite;
} }
} }

View file

@ -3,42 +3,56 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use compositing::quadtree::{Quadtree, Normal, Invalid, Hidden}; use compositing::quadtree::{Quadtree, Normal, Invalid, Hidden};
use constellation::{SendableChildFrameTree, SendableFrameTree}; use pipeline::CompositionPipeline;
use windowing::{MouseWindowEvent, MouseWindowClickEvent, MouseWindowMouseDownEvent};
use windowing::{MouseWindowMouseUpEvent};
use azure::azure_hl::Color;
use geom::matrix::identity; use geom::matrix::identity;
use geom::point::Point2D; use geom::point::Point2D;
use geom::rect::Rect; use geom::rect::Rect;
use geom::size::Size2D; use geom::size::Size2D;
use gfx::render_task::{ReRenderMsg, UnusedBufferMsg}; use gfx::render_task::{ReRenderMsg, UnusedBufferMsg};
use gfx;
use layers::layers::{ContainerLayerKind, ContainerLayer, Flip, NoFlip, TextureLayer}; use layers::layers::{ContainerLayerKind, ContainerLayer, Flip, NoFlip, TextureLayer};
use layers::layers::TextureLayerKind; use layers::layers::TextureLayerKind;
#[cfg(target_os="macos")]
#[cfg(target_os="android")]
use layers::layers::VerticalFlip;
use layers::platform::surface::{NativeCompositingGraphicsContext, NativeSurfaceMethods}; use layers::platform::surface::{NativeCompositingGraphicsContext, NativeSurfaceMethods};
use layers::texturegl::{Texture, TextureTarget}; use layers::texturegl::{Texture, TextureTarget};
#[cfg(target_os="macos")] use layers::texturegl::TextureTargetRectangle;
use pipeline::CompositionPipeline;
use script::dom::event::{ClickEvent, MouseDownEvent, MouseMoveEvent, MouseUpEvent}; use script::dom::event::{ClickEvent, MouseDownEvent, MouseMoveEvent, MouseUpEvent};
use script::script_task::{ScriptChan, SendEventMsg}; use script::script_task::{ScriptChan, SendEventMsg};
use servo_msg::compositor_msg::{LayerBuffer, LayerBufferSet, Epoch, Tile}; use servo_msg::compositor_msg::{Epoch, FixedPosition, LayerBuffer, LayerBufferSet, LayerId};
use servo_msg::compositor_msg::{ScrollPolicy, Tile};
use servo_msg::constellation_msg::PipelineId; use servo_msg::constellation_msg::PipelineId;
use std::cmp; use std::cmp;
use std::rc::Rc; use std::rc::Rc;
use windowing::{MouseWindowEvent, MouseWindowClickEvent, MouseWindowMouseDownEvent};
use windowing::{MouseWindowMouseUpEvent};
use azure::azure_hl::Color;
use gfx;
#[cfg(target_os="macos")]
#[cfg(target_os="android")]
use layers::layers::VerticalFlip;
#[cfg(not(target_os="macos"))] #[cfg(not(target_os="macos"))]
use layers::texturegl::TextureTarget2D; use layers::texturegl::TextureTarget2D;
#[cfg(target_os="macos")]
use layers::texturegl::TextureTargetRectangle;
/// The amount of memory usage allowed per layer.
static MAX_TILE_MEMORY_PER_LAYER: uint = 10000000;
/// The CompositorLayer represents an element on a page that has a unique scroll /// The CompositorLayer represents an element on a page that has a unique scroll
/// or animation behavior. This can include absolute positioned elements, iframes, etc. /// or animation behavior. This can include absolute positioned elements, iframes, etc.
/// Each layer can also have child layers. /// Each layer can also have child layers.
///
/// FIXME(#2003, pcwalton): This should be merged with the concept of a layer in `rust-layers` and
/// ultimately removed, except as a set of helper methods on `rust-layers` layers.
pub struct CompositorLayer { pub struct CompositorLayer {
/// This layer's pipeline. BufferRequests and mouse events will be sent through this. /// This layer's pipeline. BufferRequests and mouse events will be sent through this.
pipeline: CompositionPipeline, pipeline: CompositionPipeline,
/// The ID of this layer within the pipeline.
id: LayerId,
/// The bounds of this layer in terms of its parent (a.k.a. the scissor box).
bounds: Rect<f32>,
/// The size of the underlying page in page coordinates. This is an option /// The size of the underlying page in page coordinates. This is an option
/// because we may not know the size of the page until layout is finished completely. /// because we may not know the size of the page until layout is finished completely.
/// if we have no size yet, the layer is hidden until a size message is recieved. /// if we have no size yet, the layer is hidden until a size message is recieved.
@ -70,17 +84,20 @@ pub struct CompositorLayer {
epoch: Epoch, epoch: Epoch,
/// The behavior of this layer when a scroll message is received. /// The behavior of this layer when a scroll message is received.
scroll_behavior: ScrollBehavior, wants_scroll_events: WantsScrollEventsFlag,
/// Whether an ancestor layer that receives scroll events moves this layer.
scroll_policy: ScrollPolicy,
/// True if CPU rendering is enabled, false if we're using GPU rendering. /// True if CPU rendering is enabled, false if we're using GPU rendering.
cpu_painting: bool, cpu_painting: bool,
/// The color to use for the unrendered-content void /// The color to use for the unrendered-content void
unrendered_color: Color unrendered_color: Color,
} }
/// Helper struct for keeping CompositorLayer children organized. /// Helper struct for keeping CompositorLayer children organized.
struct CompositorLayerChild { pub struct CompositorLayerChild {
/// The child itself. /// The child itself.
child: ~CompositorLayer, child: ~CompositorLayer,
/// A ContainerLayer managed by the parent node. This deals with clipping and /// A ContainerLayer managed by the parent node. This deals with clipping and
@ -95,13 +112,28 @@ enum MaybeQuadtree {
NoTree(uint, Option<uint>), NoTree(uint, Option<uint>),
} }
/// Determines the behavior of the layer when a scroll message is recieved. impl MaybeQuadtree {
enum ScrollBehavior { fn tile_size(&self) -> uint {
/// Normal scrolling behavior. match *self {
Scroll, Tree(ref quadtree) => quadtree.max_tile_size,
/// Scrolling messages targeted at this layer are ignored, but can be NoTree(tile_size, _) => tile_size,
/// passed on to child layers. }
FixedPosition, }
}
#[deriving(Eq, Clone)]
pub enum WantsScrollEventsFlag {
WantsScrollEvents,
DoesntWantScrollEvents,
}
fn create_container_layer_from_rect(rect: Rect<f32>) -> Rc<ContainerLayer> {
let container = Rc::new(ContainerLayer());
container.borrow().scissor.set(Some(rect));
container.borrow().common.with_mut(|common| {
common.transform = identity().translate(rect.origin.x, rect.origin.y, 0f32);
});
container
} }
trait Clampable { trait Clampable {
@ -124,79 +156,155 @@ impl Clampable for f32 {
impl CompositorLayer { impl CompositorLayer {
/// Creates a new CompositorLayer with an optional page size. If no page size is given, /// Creates a new `CompositorLayer`.
/// the layer is initially hidden and initialized without a quadtree. fn new(pipeline: CompositionPipeline,
pub fn new(pipeline: CompositionPipeline, layer_id: LayerId,
page_size: Option<Size2D<f32>>, bounds: Rect<f32>,
tile_size: uint, page_size: Option<Size2D<f32>>,
max_mem: Option<uint>, tile_size: uint,
cpu_painting: bool) cpu_painting: bool,
-> CompositorLayer { wants_scroll_events: WantsScrollEventsFlag,
scroll_policy: ScrollPolicy)
-> CompositorLayer {
CompositorLayer { CompositorLayer {
pipeline: pipeline, pipeline: pipeline,
id: layer_id,
bounds: bounds,
page_size: page_size, page_size: page_size,
scroll_offset: Point2D(0f32, 0f32), scroll_offset: Point2D(0f32, 0f32),
children: ~[], children: ~[],
quadtree: match page_size { quadtree: match page_size {
None => NoTree(tile_size, max_mem), None => NoTree(tile_size, Some(MAX_TILE_MEMORY_PER_LAYER)),
Some(page_size) => Tree(Quadtree::new(Size2D(page_size.width as uint, Some(page_size) => {
page_size.height as uint), Tree(Quadtree::new(Size2D(page_size.width as uint, page_size.height as uint),
tile_size, tile_size,
max_mem)), Some(MAX_TILE_MEMORY_PER_LAYER)))
}
}, },
root_layer: Rc::new(ContainerLayer()), root_layer: Rc::new(ContainerLayer()),
hidden: true, hidden: true,
epoch: Epoch(0), epoch: Epoch(0),
scroll_behavior: Scroll, wants_scroll_events: wants_scroll_events,
scroll_policy: scroll_policy,
cpu_painting: cpu_painting, cpu_painting: cpu_painting,
unrendered_color: gfx::color::rgba(0.0, 0.0, 0.0, 0.0), unrendered_color: gfx::color::rgba(0.0, 0.0, 0.0, 0.0),
} }
} }
/// Constructs a CompositorLayer tree from a frame tree. /// Creates a new root `CompositorLayer` bound to a composition pipeline with an optional page
pub fn from_frame_tree(frame_tree: SendableFrameTree, /// size. If no page size is given, the layer is initially hidden and initialized without a
tile_size: uint, /// quadtree.
max_mem: Option<uint>, pub fn new_root(pipeline: CompositionPipeline,
cpu_painting: bool) page_size: Size2D<f32>,
-> CompositorLayer { tile_size: uint,
let SendableFrameTree { pipeline, children } = frame_tree; cpu_painting: bool)
let mut layer = CompositorLayer::new(pipeline, None, tile_size, max_mem, cpu_painting); -> CompositorLayer {
layer.children = (children.move_iter().map(|child| { CompositorLayer {
let SendableChildFrameTree { frame_tree, rect } = child; pipeline: pipeline,
let container = Rc::new(ContainerLayer()); id: LayerId::null(),
match rect { bounds: Rect(Point2D(0f32, 0f32), page_size),
Some(rect) => { page_size: Some(page_size),
container.borrow().scissor.set(Some(rect)); scroll_offset: Point2D(0f32, 0f32),
container.borrow().common.with_mut(|common| common.transform = identity().translate(rect.origin.x, children: ~[],
rect.origin.y, quadtree: NoTree(tile_size, Some(MAX_TILE_MEMORY_PER_LAYER)),
0f32)); root_layer: Rc::new(ContainerLayer()),
} hidden: false,
None => {} epoch: Epoch(0),
} wants_scroll_events: WantsScrollEvents,
scroll_policy: FixedPosition,
let child_layer = ~CompositorLayer::from_frame_tree(frame_tree, cpu_painting: cpu_painting,
tile_size, unrendered_color: gfx::color::rgba(0.0, 0.0, 0.0, 0.0),
max_mem, }
cpu_painting);
ContainerLayer::add_child_start(container.clone(), ContainerLayerKind(child_layer.root_layer.clone()));
CompositorLayerChild {
child: child_layer,
container: container,
}
})).collect();
layer.set_occlusions();
layer
} }
// Move the layer by as relative specified amount in page coordinates. Does not change /// Adds a child layer to the layer with the given ID and the given pipeline, if it doesn't
// the position of the layer relative to its parent. This also takes in a cursor position /// exist yet. The child layer will have the same pipeline, tile size, memory limit, and CPU
// to see if the mouse is over child layers first. If a layer successfully scrolled, returns /// painting status as its parent.
// true; otherwise returns false, so a parent layer can scroll instead. ///
pub fn scroll(&mut self, delta: Point2D<f32>, cursor: Point2D<f32>, window_size: Size2D<f32>) /// Returns:
-> bool { /// * True if the layer was added;
/// * True if the layer was not added because it already existed;
/// * False if the layer could not be added because no suitable parent layer with the given
/// ID and pipeline could be found.
pub fn add_child_if_necessary(&mut self,
container_layer: Rc<ContainerLayer>,
pipeline_id: PipelineId,
parent_layer_id: LayerId,
child_layer_id: LayerId,
rect: Rect<f32>,
page_size: Size2D<f32>,
scroll_policy: ScrollPolicy)
-> bool {
if self.pipeline.id != pipeline_id || self.id != parent_layer_id {
return self.children.mut_iter().any(|kid_holder| {
kid_holder.child.add_child_if_necessary(kid_holder.container.clone(),
pipeline_id,
parent_layer_id,
child_layer_id,
rect,
page_size,
scroll_policy)
})
}
// See if we've already made this child layer.
if self.children.iter().any(|kid_holder| {
kid_holder.child.pipeline.id == pipeline_id &&
kid_holder.child.id == child_layer_id
}) {
return true
}
let mut kid = ~CompositorLayer::new(self.pipeline.clone(),
child_layer_id,
rect,
Some(page_size),
self.quadtree.tile_size(),
self.cpu_painting,
DoesntWantScrollEvents,
scroll_policy);
kid.hidden = false;
// Place the kid's layer in a container...
let kid_container = create_container_layer_from_rect(rect);
ContainerLayer::add_child_start(kid_container.clone(),
ContainerLayerKind(kid.root_layer.clone()));
// ...and add *that* container as a child of the container passed in.
ContainerLayer::add_child_end(container_layer,
ContainerLayerKind(kid_container.clone()));
self.children.push(CompositorLayerChild {
child: kid,
container: kid_container,
});
true
}
/// Move the layer's descendants that don't want scroll events and scroll by a relative
/// specified amount in page coordinates. This also takes in a cursor position to see if the
/// mouse is over child layers first. If a layer successfully scrolled, returns true; otherwise
/// returns false, so a parent layer can scroll instead.
pub fn handle_scroll_event(&mut self,
delta: Point2D<f32>,
cursor: Point2D<f32>,
window_size: Size2D<f32>)
-> bool {
// If this layer is hidden, neither it nor its children will scroll.
if self.hidden {
return false
}
// If this layer doesn't want scroll events, neither it nor its children can handle scroll
// events.
if self.wants_scroll_events != WantsScrollEvents {
return false
}
// Allow children to scroll.
let cursor = cursor - self.scroll_offset; let cursor = cursor - self.scroll_offset;
for child in self.children.mut_iter().filter(|x| !x.child.hidden) { for child in self.children.mut_iter() {
// NOTE: work around borrowchk // NOTE: work around borrowchk
let tmp = child.container.borrow().scissor.borrow(); let tmp = child.container.borrow().scissor.borrow();
match *tmp.get() { match *tmp.get() {
@ -206,42 +314,82 @@ impl CompositorLayer {
Some(rect) => { Some(rect) => {
if cursor.x >= rect.origin.x && cursor.x < rect.origin.x + rect.size.width if cursor.x >= rect.origin.x && cursor.x < rect.origin.x + rect.size.width
&& cursor.y >= rect.origin.y && cursor.y < rect.origin.y + rect.size.height && cursor.y >= rect.origin.y && cursor.y < rect.origin.y + rect.size.height
&& child.child.scroll(delta, cursor - rect.origin, rect.size) { && child.child.handle_scroll_event(delta,
return true; cursor - rect.origin,
rect.size) {
return true
} }
} }
} }
} }
// This scroll event is mine! // This scroll event is mine!
match self.scroll_behavior { // Scroll this layer!
Scroll => { let old_origin = self.scroll_offset;
// Scroll this layer! self.scroll_offset = self.scroll_offset + delta;
let old_origin = self.scroll_offset;
self.scroll_offset = self.scroll_offset + delta;
// bounds checking // bounds checking
let page_size = match self.page_size { let page_size = match self.page_size {
Some(size) => size, Some(size) => size,
None => fail!("CompositorLayer: tried to scroll with no page size set"), None => fail!("CompositorLayer: tried to scroll with no page size set"),
}; };
let min_x = cmp::min(window_size.width - page_size.width, 0.0); let min_x = cmp::min(window_size.width - page_size.width, 0.0);
self.scroll_offset.x = self.scroll_offset.x.clamp(&min_x, &0.0); self.scroll_offset.x = self.scroll_offset.x.clamp(&min_x, &0.0);
let min_y = cmp::min(window_size.height - page_size.height, 0.0); let min_y = cmp::min(window_size.height - page_size.height, 0.0);
self.scroll_offset.y = self.scroll_offset.y.clamp(&min_y, &0.0); self.scroll_offset.y = self.scroll_offset.y.clamp(&min_y, &0.0);
// check to see if we scrolled if old_origin - self.scroll_offset == Point2D(0f32, 0f32) {
if old_origin - self.scroll_offset == Point2D(0f32, 0f32) { return false
return false;
}
self.root_layer.borrow().common.with_mut(|common| common.set_transform(identity().translate(self.scroll_offset.x,
self.scroll_offset.y,
0.0)));
true
}
FixedPosition => false, // Ignore this scroll event.
} }
self.scroll(self.scroll_offset)
}
#[allow(dead_code)]
fn dump_layer_tree(&self, layer: Rc<ContainerLayer>, indent: ~str) {
{
let scissor = layer.borrow().scissor.borrow();
println!("{}scissor {:?}", indent, *scissor.get());
}
for kid in layer.borrow().children() {
match kid {
ContainerLayerKind(ref container_layer) => {
self.dump_layer_tree((*container_layer).clone(), indent + " ");
}
TextureLayerKind(_) => {
println!("{} (texture layer)", indent);
}
}
}
}
/// Actually scrolls the descendants of a layer that scroll. This is called by
/// `handle_scroll_event` above when it determines that a layer wants to scroll.
fn scroll(&mut self, scroll_offset: Point2D<f32>) -> bool {
let mut result = false;
// Only scroll this layer if it's not fixed-positioned.
if self.scroll_policy != FixedPosition {
// Scroll this layer!
self.scroll_offset = scroll_offset;
self.root_layer
.borrow()
.common
.with_mut(|common| {
common.set_transform(identity().translate(self.scroll_offset.x,
self.scroll_offset.y,
0.0))
});
result = true
}
for kid_holder in self.children.mut_iter() {
result = kid_holder.child.scroll(scroll_offset) || result;
}
result
} }
// Takes in a MouseWindowEvent, determines if it should be passed to children, and // Takes in a MouseWindowEvent, determines if it should be passed to children, and
@ -282,43 +430,49 @@ impl CompositorLayer {
chan.try_send(SendEventMsg(self.pipeline.id.clone(), message)); chan.try_send(SendEventMsg(self.pipeline.id.clone(), message));
} }
// Given the current window size, determine which tiles need to be (re)rendered // Given the current window size, determine which tiles need to be (re-)rendered and sends them
// and sends them off the the appropriate renderer. // off the the appropriate renderer. Returns true if and only if the scene should be repainted.
// Returns a bool that is true if the scene should be repainted.
pub fn get_buffer_request(&mut self, pub fn get_buffer_request(&mut self,
graphics_context: &NativeCompositingGraphicsContext, graphics_context: &NativeCompositingGraphicsContext,
window_rect: Rect<f32>, window_rect: Rect<f32>,
scale: f32) scale: f32)
-> bool { -> bool {
let rect = Rect(Point2D(-self.scroll_offset.x + window_rect.origin.x, let mut redisplay = false;
-self.scroll_offset.y + window_rect.origin.y), match self.quadtree {
window_rect.size); NoTree(..) => {}
let mut redisplay: bool; Tree(ref mut quadtree) => {
{ // block here to prevent double mutable borrow of self let (request, unused) = quadtree.get_tile_rects_page(window_rect, scale);
let quadtree = match self.quadtree {
NoTree(..) => fail!("CompositorLayer: cannot get buffer request for {:?}, // Workaround to make redisplay visible outside block.
no quadtree initialized", self.pipeline.id), redisplay = !unused.is_empty();
Tree(ref mut quadtree) => quadtree, if redisplay {
}; // Send back unused tiles.
let (request, unused) = quadtree.get_tile_rects_page(rect, scale); self.pipeline.render_chan.try_send(UnusedBufferMsg(unused));
redisplay = !unused.is_empty(); // workaround to make redisplay visible outside block }
if redisplay { // send back unused tiles if !request.is_empty() {
self.pipeline.render_chan.try_send(UnusedBufferMsg(unused)); // Ask for tiles.
//
// FIXME(#2003, pcwalton): We may want to batch these up in the case in which
// one page has multiple layers, to avoid the user seeing inconsistent states.
let msg = ReRenderMsg(request, scale, self.id, self.epoch);
self.pipeline.render_chan.try_send(msg);
}
} }
if !request.is_empty() { // ask for tiles };
self.pipeline.render_chan.try_send(ReRenderMsg(request, scale, self.epoch));
}
}
if redisplay { if redisplay {
self.build_layer_tree(graphics_context); self.build_layer_tree(graphics_context);
} }
let transform = |x: &mut CompositorLayerChild| -> bool { let transform = |x: &mut CompositorLayerChild| -> bool {
// NOTE: work around borrowchk // NOTE: work around borrowchk
let tmp = x.container.borrow().scissor.borrow(); let tmp = x.container.borrow().scissor.borrow();
match *tmp.get() { match *tmp.get() {
Some(scissor) => { Some(scissor) => {
let new_rect = rect.intersection(&scissor); let mut new_rect = window_rect;
match new_rect { new_rect.origin.x = new_rect.origin.x - x.child.scroll_offset.x;
new_rect.origin.y = new_rect.origin.y - x.child.scroll_offset.y;
match new_rect.intersection(&scissor) {
Some(new_rect) => { Some(new_rect) => {
// Child layers act as if they are rendered at (0,0), so we // Child layers act as if they are rendered at (0,0), so we
// subtract the layer's (x,y) coords in its containing page // subtract the layer's (x,y) coords in its containing page
@ -328,13 +482,11 @@ impl CompositorLayer {
x.child.get_buffer_request(graphics_context, child_rect, scale) x.child.get_buffer_request(graphics_context, child_rect, scale)
} }
None => { None => {
false //Layer is offscreen false // Layer is offscreen
} }
} }
} }
None => { None => fail!("child layer not clipped!"),
fail!("CompositorLayer: Child layer not clipped");
}
} }
}; };
self.children.mut_iter().filter(|x| !x.child.hidden) self.children.mut_iter().filter(|x| !x.child.hidden)
@ -347,9 +499,18 @@ impl CompositorLayer {
// and clip the layer to the specified size in page coordinates. // and clip the layer to the specified size in page coordinates.
// If the layer is hidden and has a defined page size, unhide it. // If the layer is hidden and has a defined page size, unhide it.
// This method returns false if the specified layer is not found. // This method returns false if the specified layer is not found.
pub fn set_clipping_rect(&mut self, pipeline_id: PipelineId, new_rect: Rect<f32>) -> bool { pub fn set_clipping_rect(&mut self,
match self.children.iter().position(|x| pipeline_id == x.child.pipeline.id) { pipeline_id: PipelineId,
layer_id: LayerId,
new_rect: Rect<f32>)
-> bool {
debug!("compositor_layer: starting set_clipping_rect()");
match self.children.iter().position(|kid_holder| {
pipeline_id == kid_holder.child.pipeline.id &&
layer_id == kid_holder.child.id
}) {
Some(i) => { Some(i) => {
debug!("compositor_layer: node found for set_clipping_rect()");
let child_node = &mut self.children[i]; let child_node = &mut self.children[i];
child_node.container.borrow().common.with_mut(|common| child_node.container.borrow().common.with_mut(|common|
common.set_transform(identity().translate(new_rect.origin.x, common.set_transform(identity().translate(new_rect.origin.x,
@ -360,6 +521,7 @@ impl CompositorLayer {
let tmp = child_node.container.borrow().scissor.borrow(); let tmp = child_node.container.borrow().scissor.borrow();
tmp.get().clone() tmp.get().clone()
}; };
child_node.container.borrow().scissor.set(Some(new_rect)); child_node.container.borrow().scissor.set(Some(new_rect));
match self.quadtree { match self.quadtree {
NoTree(..) => {} // Nothing to do NoTree(..) => {} // Nothing to do
@ -382,72 +544,91 @@ impl CompositorLayer {
None => { None => {
// ID does not match any of our immediate children, so recurse on // ID does not match any of our immediate children, so recurse on
// descendents (including hidden children) // descendents (including hidden children)
self.children.mut_iter().map(|x| &mut x.child).any(|x| x.set_clipping_rect(pipeline_id, new_rect)) self.children
.mut_iter()
.map(|kid_holder| &mut kid_holder.child)
.any(|kid| kid.set_clipping_rect(pipeline_id, layer_id, new_rect))
} }
} }
} }
// Set the layer's page size. This signals that the renderer is ready for BufferRequests. // Set the layer's page size. This signals that the renderer is ready for BufferRequests.
// If the layer is hidden and has a defined clipping rect, unhide it. // If the layer is hidden and has a defined clipping rect, unhide it.
// This method returns false if the specified layer is not found. // This method returns false if the specified layer is not found.
pub fn resize(&mut self, pipeline_id: PipelineId, new_size: Size2D<f32>, window_size: Size2D<f32>, epoch: Epoch) -> bool { pub fn resize(&mut self,
if self.pipeline.id == pipeline_id { pipeline_id: PipelineId,
self.epoch = epoch; layer_id: LayerId,
self.page_size = Some(new_size); new_size: Size2D<f32>,
match self.quadtree { window_size: Size2D<f32>,
Tree(ref mut quadtree) => { epoch: Epoch)
self.pipeline.render_chan.try_send(UnusedBufferMsg(quadtree.resize(new_size.width as uint, -> bool {
new_size.height as uint))); debug!("compositor_layer: starting resize()");
} if self.pipeline.id != pipeline_id || self.id != layer_id {
NoTree(tile_size, max_mem) => { return self.resize_helper(pipeline_id, layer_id, new_size, epoch)
self.quadtree = Tree(Quadtree::new(Size2D(new_size.width as uint,
new_size.height as uint),
tile_size,
max_mem))
}
}
// Call scroll for bounds checking if the page shrunk. Use (-1, -1) as the cursor position
// to make sure the scroll isn't propagated downwards.
self.scroll(Point2D(0f32, 0f32), Point2D(-1f32, -1f32), window_size);
self.hidden = false;
self.set_occlusions();
true
} else {
self.resize_helper(pipeline_id, new_size, epoch)
} }
debug!("compositor_layer: layer found for resize()");
self.epoch = epoch;
self.page_size = Some(new_size);
match self.quadtree {
Tree(ref mut quadtree) => {
self.pipeline
.render_chan
.try_send(UnusedBufferMsg(quadtree.resize(new_size.width as uint,
new_size.height as uint)));
}
NoTree(tile_size, max_mem) => {
self.quadtree = Tree(Quadtree::new(Size2D(new_size.width as uint,
new_size.height as uint),
tile_size,
max_mem))
}
}
// Call scroll for bounds checking if the page shrunk. Use (-1, -1) as the cursor position
// to make sure the scroll isn't propagated downwards.
self.handle_scroll_event(Point2D(0f32, 0f32), Point2D(-1f32, -1f32), window_size);
self.hidden = false;
self.set_occlusions();
true
} }
pub fn move(&mut self, origin: Point2D<f32>, window_size: Size2D<f32>) -> bool { pub fn move(&mut self,
match self.scroll_behavior { pipeline_id: PipelineId,
Scroll => { layer_id: LayerId,
// Scroll this layer! origin: Point2D<f32>,
let old_origin = self.scroll_offset; window_size: Size2D<f32>)
self.scroll_offset = Point2D(0f32, 0f32) - origin; -> bool {
// Search children for the right layer to move.
// bounds checking if self.pipeline.id != pipeline_id || self.id != layer_id {
let page_size = match self.page_size { return self.children.mut_iter().any(|kid_holder| {
Some(size) => size, kid_holder.child.move(pipeline_id, layer_id, origin, window_size)
None => fail!("CompositorLayer: tried to scroll with no page size set"), })
};
let min_x = cmp::min(window_size.width - page_size.width, 0.0);
self.scroll_offset.x = self.scroll_offset.x.clamp(&min_x, &0.0);
let min_y = cmp::min(window_size.height - page_size.height, 0.0);
self.scroll_offset.y = self.scroll_offset.y.clamp(&min_y, &0.0);
// check to see if we scrolled
if old_origin - self.scroll_offset == Point2D(0f32, 0f32) {
return false;
}
self.root_layer.borrow().common.with_mut(|common|
common.set_transform(identity().translate(self.scroll_offset.x,
self.scroll_offset.y,
0.0)));
true
}
FixedPosition => false // Ignore this scroll event.
} }
if self.wants_scroll_events != WantsScrollEvents {
return false
}
// Scroll this layer!
let old_origin = self.scroll_offset;
self.scroll_offset = Point2D(0f32, 0f32) - origin;
// bounds checking
let page_size = match self.page_size {
Some(size) => size,
None => fail!("CompositorLayer: tried to scroll with no page size set"),
};
let min_x = cmp::min(window_size.width - page_size.width, 0.0);
self.scroll_offset.x = self.scroll_offset.x.clamp(&min_x, &0.0);
let min_y = cmp::min(window_size.height - page_size.height, 0.0);
self.scroll_offset.y = self.scroll_offset.y.clamp(&min_y, &0.0);
// check to see if we scrolled
if old_origin - self.scroll_offset == Point2D(0f32, 0f32) {
return false;
}
self.scroll(self.scroll_offset)
} }
// Returns whether the layer should be vertically flipped. // Returns whether the layer should be vertically flipped.
@ -479,9 +660,19 @@ impl CompositorLayer {
} }
// A helper method to resize sublayers. // A helper method to resize sublayers.
fn resize_helper(&mut self, pipeline_id: PipelineId, new_size: Size2D<f32>, epoch: Epoch) -> bool { fn resize_helper(&mut self,
let found = match self.children.iter().position(|x| pipeline_id == x.child.pipeline.id) { pipeline_id: PipelineId,
layer_id: LayerId,
new_size: Size2D<f32>,
epoch: Epoch)
-> bool {
debug!("compositor_layer: starting resize_helper()");
let found = match self.children.iter().position(|kid_holder| {
pipeline_id == kid_holder.child.pipeline.id &&
layer_id == kid_holder.child.id
}) {
Some(i) => { Some(i) => {
debug!("compositor_layer: layer found for resize_helper()");
let child_node = &mut self.children[i]; let child_node = &mut self.children[i];
let child = &mut child_node.child; let child = &mut child_node.child;
child.epoch = epoch; child.epoch = epoch;
@ -502,9 +693,11 @@ impl CompositorLayer {
let tmp = child_node.container.borrow().scissor.borrow(); let tmp = child_node.container.borrow().scissor.borrow();
match *tmp.get() { match *tmp.get() {
Some(scissor) => { Some(scissor) => {
// Call scroll for bounds checking if the page shrunk. Use (-1, -1) as the cursor position // Call scroll for bounds checking if the page shrunk. Use (-1, -1) as the
// to make sure the scroll isn't propagated downwards. // cursor position to make sure the scroll isn't propagated downwards.
child.scroll(Point2D(0f32, 0f32), Point2D(-1f32, -1f32), scissor.size); child.handle_scroll_event(Point2D(0f32, 0f32),
Point2D(-1f32, -1f32),
scissor.size);
child.hidden = false; child.hidden = false;
} }
None => {} // Nothing to do None => {} // Nothing to do
@ -516,11 +709,15 @@ impl CompositorLayer {
if found { // Boolean flag to get around double borrow of self if found { // Boolean flag to get around double borrow of self
self.set_occlusions(); self.set_occlusions();
true return true
} else {
// ID does not match ours, so recurse on descendents (including hidden children)
self.children.mut_iter().map(|x| &mut x.child).any(|x| x.resize_helper(pipeline_id, new_size, epoch))
} }
// If we got here, the layer's ID does not match ours, so recurse on descendents (including
// hidden children).
self.children
.mut_iter()
.map(|kid_holder| &mut kid_holder.child)
.any(|kid_holder| kid_holder.resize_helper(pipeline_id, layer_id, new_size, epoch))
} }
// Collect buffers from the quadtree. This method IS NOT recursive, so child CompositorLayers // Collect buffers from the quadtree. This method IS NOT recursive, so child CompositorLayers
@ -577,7 +774,9 @@ impl CompositorLayer {
buffer.native_surface.bind_to_texture(graphics_context, &texture, size); buffer.native_surface.bind_to_texture(graphics_context, &texture, size);
// Make a texture layer and add it. // Make a texture layer and add it.
texture_layer = Rc::new(TextureLayer::new(texture, buffer.screen_pos.size, flip)); texture_layer = Rc::new(TextureLayer::new(texture,
buffer.screen_pos.size,
flip));
ContainerLayer::add_child_end(self.root_layer.clone(), ContainerLayer::add_child_end(self.root_layer.clone(),
TextureLayerKind(texture_layer.clone())); TextureLayerKind(texture_layer.clone()));
None None
@ -632,57 +831,65 @@ impl CompositorLayer {
pub fn add_buffers(&mut self, pub fn add_buffers(&mut self,
graphics_context: &NativeCompositingGraphicsContext, graphics_context: &NativeCompositingGraphicsContext,
pipeline_id: PipelineId, pipeline_id: PipelineId,
layer_id: LayerId,
mut new_buffers: ~LayerBufferSet, mut new_buffers: ~LayerBufferSet,
epoch: Epoch) epoch: Epoch)
-> Option<~LayerBufferSet> { -> Option<~LayerBufferSet> {
if self.pipeline.id == pipeline_id { debug!("compositor_layer: starting add_buffers()");
if self.epoch != epoch { if self.pipeline.id != pipeline_id || self.id != layer_id {
debug!("compositor epoch mismatch: {:?} != {:?}, id: {:?}", // ID does not match ours, so recurse on descendents (including hidden children).
self.epoch, for child_layer in self.children.mut_iter() {
epoch, match child_layer.child.add_buffers(graphics_context,
self.pipeline.id); pipeline_id,
self.pipeline.render_chan.try_send(UnusedBufferMsg(new_buffers.buffers)); layer_id,
return None; new_buffers,
epoch) {
None => return None,
Some(buffers) => new_buffers = buffers,
}
} }
{ // Not found. Give the caller the buffers back.
// Block here to prevent double mutable borrow of self. return Some(new_buffers)
let quadtree = match self.quadtree {
NoTree(..) => fail!("CompositorLayer: cannot add buffers, no quadtree initialized"),
Tree(ref mut quadtree) => quadtree,
};
let mut unused_tiles = ~[];
// move_rev_iter is more efficient
for buffer in new_buffers.buffers.move_rev_iter() {
unused_tiles.push_all_move(quadtree.add_tile_pixel(buffer.screen_pos.origin.x,
buffer.screen_pos.origin.y,
buffer.resolution, buffer));
}
if !unused_tiles.is_empty() { // send back unused buffers
self.pipeline.render_chan.try_send(UnusedBufferMsg(unused_tiles));
}
}
self.build_layer_tree(graphics_context);
return None;
} }
// ID does not match ours, so recurse on descendents (including hidden children). debug!("compositor_layer: layers found for add_buffers()");
for child_layer in self.children.mut_iter() {
match child_layer.child.add_buffers(graphics_context, if self.epoch != epoch {
pipeline_id, debug!("add_buffers: compositor epoch mismatch: {:?} != {:?}, id: {:?}",
new_buffers, self.epoch,
epoch) { epoch,
None => return None, self.pipeline.id);
Some(buffers) => new_buffers = buffers, self.pipeline.render_chan.try_send(UnusedBufferMsg(new_buffers.buffers));
return None
}
{
let quadtree = match self.quadtree {
NoTree(..) => {
fail!("CompositorLayer: cannot add buffers, no quadtree initialized")
}
Tree(ref mut quadtree) => quadtree,
};
let mut unused_tiles = ~[];
for buffer in new_buffers.buffers.move_rev_iter() {
unused_tiles.push_all_move(quadtree.add_tile_pixel(buffer.screen_pos.origin.x,
buffer.screen_pos.origin.y,
buffer.resolution,
buffer));
}
if !unused_tiles.is_empty() { // send back unused buffers
self.pipeline.render_chan.try_send(UnusedBufferMsg(unused_tiles));
} }
} }
// Not found. Give the caller the buffers back. self.build_layer_tree(graphics_context);
Some(new_buffers) return None
} }
// Deletes a specified sublayer, including hidden children. Returns false if the layer is not found. // Deletes a specified sublayer, including hidden children. Returns false if the layer is not
// found.
pub fn delete(&mut self, pub fn delete(&mut self,
graphics_context: &NativeCompositingGraphicsContext, graphics_context: &NativeCompositingGraphicsContext,
pipeline_id: PipelineId) pipeline_id: PipelineId)
@ -717,8 +924,11 @@ impl CompositorLayer {
} }
} }
pub fn invalidate_rect(&mut self, pipeline_id: PipelineId, rect: Rect<f32>) -> bool { pub fn invalidate_rect(&mut self, pipeline_id: PipelineId, layer_id: LayerId, rect: Rect<f32>)
if self.pipeline.id == pipeline_id { -> bool {
debug!("compositor_layer: starting invalidate_rect()");
if self.pipeline.id == pipeline_id && layer_id == self.id {
debug!("compositor_layer: layer found for invalidate_rect()");
let quadtree = match self.quadtree { let quadtree = match self.quadtree {
NoTree(..) => return true, // Nothing to do NoTree(..) => return true, // Nothing to do
Tree(ref mut quadtree) => quadtree, Tree(ref mut quadtree) => quadtree,
@ -727,7 +937,10 @@ impl CompositorLayer {
true true
} else { } else {
// ID does not match ours, so recurse on descendents (including hidden children). // ID does not match ours, so recurse on descendents (including hidden children).
self.children.mut_iter().map(|x| &mut x.child).any(|x| x.invalidate_rect(pipeline_id, rect)) self.children
.mut_iter()
.map(|kid_holder| &mut kid_holder.child)
.any(|kid| kid.invalidate_rect(pipeline_id, layer_id, rect))
} }
} }
@ -773,16 +986,6 @@ impl CompositorLayer {
} }
} }
/// Destroys all quadtree tiles of all layers, including child layers, sending the buffers
/// back to the renderer to be destroyed or reused.
pub fn clear_all(&mut self) {
self.clear();
for kid in self.children.mut_iter() {
kid.child.clear_all()
}
}
/// Destroys all tiles of all layers, including children, *without* sending them back to the /// Destroys all tiles of all layers, including children, *without* sending them back to the
/// renderer. You must call this only when the render task is destined to be going down; /// renderer. You must call this only when the render task is destined to be going down;
/// otherwise, you will leak tiles. /// otherwise, you will leak tiles.
@ -804,5 +1007,9 @@ impl CompositorLayer {
kid.child.forget_all_tiles(); kid.child.forget_all_tiles();
} }
} }
pub fn id_of_first_child(&self) -> LayerId {
self.children.iter().next().expect("no first child!").child.id
}
} }

View file

@ -13,8 +13,8 @@ use geom::point::Point2D;
use geom::rect::Rect; use geom::rect::Rect;
use geom::size::Size2D; use geom::size::Size2D;
use layers::platform::surface::{NativeCompositingGraphicsContext, NativeGraphicsMetadata}; use layers::platform::surface::{NativeCompositingGraphicsContext, NativeGraphicsMetadata};
use servo_msg::compositor_msg::{Epoch, RenderListener, LayerBufferSet, RenderState, ReadyState}; use servo_msg::compositor_msg::{Epoch, LayerBufferSet, LayerId, LayerMetadata, ReadyState};
use servo_msg::compositor_msg::ScriptListener; use servo_msg::compositor_msg::{RenderListener, RenderState, ScriptListener, ScrollPolicy};
use servo_msg::constellation_msg::{ConstellationChan, PipelineId}; use servo_msg::constellation_msg::{ConstellationChan, PipelineId};
use servo_util::opts::Opts; use servo_util::opts::Opts;
use servo_util::time::ProfilerChan; use servo_util::time::ProfilerChan;
@ -45,12 +45,15 @@ impl ScriptListener for CompositorChan {
self.chan.send(msg); self.chan.send(msg);
} }
fn invalidate_rect(&self, id: PipelineId, rect: Rect<uint>) { fn invalidate_rect(&self, pipeline_id: PipelineId, layer_id: LayerId, rect: Rect<uint>) {
self.chan.send(InvalidateRect(id, rect)); self.chan.send(InvalidateRect(pipeline_id, layer_id, rect));
} }
fn scroll_fragment_point(&self, id: PipelineId, point: Point2D<f32>) { fn scroll_fragment_point(&self,
self.chan.send(ScrollFragmentPoint(id, point)); pipeline_id: PipelineId,
layer_id: LayerId,
point: Point2D<f32>) {
self.chan.send(ScrollFragmentPoint(pipeline_id, layer_id, point));
} }
fn close(&self) { fn close(&self) {
@ -72,30 +75,68 @@ impl RenderListener for CompositorChan {
port.recv() port.recv()
} }
fn paint(&self, id: PipelineId, layer_buffer_set: ~LayerBufferSet, epoch: Epoch) { fn paint(&self,
self.chan.send(Paint(id, layer_buffer_set, epoch)) pipeline_id: PipelineId,
layer_id: LayerId,
layer_buffer_set: ~LayerBufferSet,
epoch: Epoch) {
self.chan.send(Paint(pipeline_id, layer_id, layer_buffer_set, epoch))
} }
fn new_layer(&self, id: PipelineId, page_size: Size2D<uint>) { fn create_layer_group_for_pipeline(&self, id: PipelineId, page_size: Size2D<uint>) {
let Size2D { width, height } = page_size; let Size2D { width, height } = page_size;
self.chan.send(NewLayer(id, Size2D(width as f32, height as f32))) self.chan.send(CreateRootCompositorLayerIfNecessary(id,
} LayerId::null(),
fn set_layer_page_size_and_color(&self, id: PipelineId, page_size: Size2D<uint>, epoch: Epoch, color: Color) { Size2D(width as f32, height as f32)))
let Size2D { width, height } = page_size;
self.chan.send(SetUnRenderedColor(id, color));
self.chan.send(SetLayerPageSize(id, Size2D(width as f32, height as f32), epoch))
} }
fn set_layer_clip_rect(&self, id: PipelineId, new_rect: Rect<uint>) { fn initialize_layers_for_pipeline(&self,
pipeline_id: PipelineId,
metadata: ~[LayerMetadata],
epoch: Epoch) {
// FIXME(#2004, pcwalton): This assumes that the first layer determines the page size, and
// that all other layers are immediate children of it. This is sufficient to handle
// `position: fixed` but will not be sufficient to handle `overflow: scroll` or transforms.
let mut first = true;
for metadata in metadata.iter() {
let origin = Point2D(metadata.position.origin.x as f32,
metadata.position.origin.y as f32);
let size = Size2D(metadata.position.size.width as f32,
metadata.position.size.height as f32);
let rect = Rect(origin, size);
if first {
self.chan.send(CreateRootCompositorLayerIfNecessary(pipeline_id,
metadata.id,
size));
first = false
} else {
self.chan
.send(CreateDescendantCompositorLayerIfNecessary(pipeline_id,
metadata.id,
rect,
metadata.scroll_policy));
}
self.chan.send(SetUnRenderedColor(pipeline_id,
metadata.id,
metadata.background_color));
self.chan.send(SetLayerPageSize(pipeline_id, metadata.id, size, epoch));
}
}
fn set_layer_clip_rect(&self,
pipeline_id: PipelineId,
layer_id: LayerId,
new_rect: Rect<uint>) {
let new_rect = Rect(Point2D(new_rect.origin.x as f32, let new_rect = Rect(Point2D(new_rect.origin.x as f32,
new_rect.origin.y as f32), new_rect.origin.y as f32),
Size2D(new_rect.size.width as f32, Size2D(new_rect.size.width as f32,
new_rect.size.height as f32)); new_rect.size.height as f32));
self.chan.send(SetLayerClipRect(id, new_rect)) self.chan.send(SetLayerClipRect(pipeline_id, layer_id, new_rect))
} }
fn delete_layer(&self, id: PipelineId) { fn delete_layer_group(&self, id: PipelineId) {
self.chan.send(DeleteLayer(id)) self.chan.send(DeleteLayerGroup(id))
} }
fn set_render_state(&self, render_state: RenderState) { fn set_render_state(&self, render_state: RenderState) {
@ -133,29 +174,32 @@ pub enum Msg {
/// The headless compositor returns `None`. /// The headless compositor returns `None`.
GetGraphicsMetadata(Chan<Option<NativeGraphicsMetadata>>), GetGraphicsMetadata(Chan<Option<NativeGraphicsMetadata>>),
/// Alerts the compositor that there is a new layer to be rendered. /// Tells the compositor to create the root layer for a pipeline if necessary (i.e. if no layer
NewLayer(PipelineId, Size2D<f32>), /// with that ID exists).
/// Alerts the compositor that the specified layer's page has changed size. CreateRootCompositorLayerIfNecessary(PipelineId, LayerId, Size2D<f32>),
SetLayerPageSize(PipelineId, Size2D<f32>, Epoch), /// Tells the compositor to create a descendant layer for a pipeline if necessary (i.e. if no
/// layer with that ID exists).
CreateDescendantCompositorLayerIfNecessary(PipelineId, LayerId, Rect<f32>, ScrollPolicy),
/// Alerts the compositor that the specified layer has changed size.
SetLayerPageSize(PipelineId, LayerId, Size2D<f32>, Epoch),
/// Alerts the compositor that the specified layer's clipping rect has changed. /// Alerts the compositor that the specified layer's clipping rect has changed.
SetLayerClipRect(PipelineId, Rect<f32>), SetLayerClipRect(PipelineId, LayerId, Rect<f32>),
/// Alerts the compositor that the specified layer has been deleted. /// Alerts the compositor that the specified pipeline has been deleted.
DeleteLayer(PipelineId), DeleteLayerGroup(PipelineId),
/// Invalidate a rect for a given layer /// Invalidate a rect for a given layer
InvalidateRect(PipelineId, Rect<uint>), InvalidateRect(PipelineId, LayerId, Rect<uint>),
/// Scroll a page in a window /// Scroll a page in a window
ScrollFragmentPoint(PipelineId, Point2D<f32>), ScrollFragmentPoint(PipelineId, LayerId, Point2D<f32>),
/// Requests that the compositor paint the given layer buffer set for the given page size. /// Requests that the compositor paint the given layer buffer set for the given page size.
Paint(PipelineId, ~LayerBufferSet, Epoch), Paint(PipelineId, LayerId, ~LayerBufferSet, Epoch),
/// Alerts the compositor to the current status of page loading. /// Alerts the compositor to the current status of page loading.
ChangeReadyState(ReadyState), ChangeReadyState(ReadyState),
/// Alerts the compositor to the current status of rendering. /// Alerts the compositor to the current status of rendering.
ChangeRenderState(RenderState), ChangeRenderState(RenderState),
/// Sets the channel to the current layout and render tasks, along with their id /// Sets the channel to the current layout and render tasks, along with their id
SetIds(SendableFrameTree, Chan<()>, ConstellationChan), SetIds(SendableFrameTree, Chan<()>, ConstellationChan),
/// Sets the color of unrendered content for a layer.
SetUnRenderedColor(PipelineId, Color), SetUnRenderedColor(PipelineId, LayerId, Color),
/// The load of a page for a given URL has completed. /// The load of a page for a given URL has completed.
LoadComplete(PipelineId, Url), LoadComplete(PipelineId, Url),
} }

View file

@ -77,10 +77,11 @@ impl NullCompositor {
// we'll notice and think about whether it needs a response, like // we'll notice and think about whether it needs a response, like
// SetIds. // SetIds.
NewLayer(..) | SetLayerPageSize(..) | SetLayerClipRect(..) | DeleteLayer(..) | CreateRootCompositorLayerIfNecessary(..) |
Paint(..) | InvalidateRect(..) | ChangeReadyState(..) | ChangeRenderState(..)| CreateDescendantCompositorLayerIfNecessary(..) | SetLayerPageSize(..) |
ScrollFragmentPoint(..) | SetUnRenderedColor(..) | LoadComplete(..) SetLayerClipRect(..) | DeleteLayerGroup(..) | Paint(..) | InvalidateRect(..) |
=> () ChangeReadyState(..) | ChangeRenderState(..) | ScrollFragmentPoint(..) |
SetUnRenderedColor(..) | LoadComplete(..) => ()
} }
} }
} }

View file

@ -460,9 +460,13 @@ impl<T: Tile> QuadtreeNode<T> {
/// treated as invalid as well. /// treated as invalid as well.
/// NOTE: this method will sometimes modify the tree by deleting tiles. /// NOTE: this method will sometimes modify the tree by deleting tiles.
/// See the QuadTree function description for more details. /// See the QuadTree function description for more details.
fn get_tile_rects(&mut self, window: Rect<f32>, clip: Size2D<f32>, scale: f32, tile_size: f32, override: bool) -> fn get_tile_rects(&mut self,
(~[BufferRequest], ~[T], int) { window: Rect<f32>,
clip: Size2D<f32>,
scale: f32,
tile_size: f32,
override: bool)
-> (~[BufferRequest], ~[T], int) {
let w_x = window.origin.x; let w_x = window.origin.x;
let w_y = window.origin.y; let w_y = window.origin.y;
let w_width = window.size.width; let w_width = window.size.width;
@ -470,11 +474,11 @@ impl<T: Tile> QuadtreeNode<T> {
let s_x = self.origin.x; let s_x = self.origin.x;
let s_y = self.origin.y; let s_y = self.origin.y;
let s_size = self.size; let s_size = self.size;
// if window is outside of visible region, nothing to do // if window is outside of visible region, nothing to do
if w_x + w_width < s_x || w_x > s_x + s_size if w_x + w_width < s_x || w_x > s_x + s_size
|| w_y + w_height < s_y || w_y > s_y + s_size || w_y + w_height < s_y || w_y > s_y + s_size
|| w_x >= clip.width || w_y >= clip.height { || w_x >= clip.width || w_y >= clip.height {
return (~[], ~[], 0); return (~[], ~[], 0);
} }

View file

@ -14,6 +14,7 @@ use script::script_task::{ResizeMsg, ResizeInactiveMsg, ExitPipelineMsg};
use script::layout_interface; use script::layout_interface;
use script::layout_interface::LayoutChan; use script::layout_interface::LayoutChan;
use script::script_task::ScriptChan; use script::script_task::ScriptChan;
use servo_msg::compositor_msg::LayerId;
use servo_msg::constellation_msg::{ConstellationChan, ExitMsg, FailureMsg, Failure, FrameRectMsg}; use servo_msg::constellation_msg::{ConstellationChan, ExitMsg, FailureMsg, Failure, FrameRectMsg};
use servo_msg::constellation_msg::{IFrameSandboxState, IFrameUnsandboxed, InitLoadUrlMsg}; use servo_msg::constellation_msg::{IFrameSandboxState, IFrameUnsandboxed, InitLoadUrlMsg};
use servo_msg::constellation_msg::{LoadCompleteMsg, LoadIframeUrlMsg, LoadUrlMsg, Msg, NavigateMsg}; use servo_msg::constellation_msg::{LoadCompleteMsg, LoadIframeUrlMsg, LoadUrlMsg, Msg, NavigateMsg};
@ -98,15 +99,6 @@ pub struct SendableChildFrameTree {
rect: Option<Rect<f32>>, rect: Option<Rect<f32>>,
} }
// impl SendableFrameTree {
// fn contains(&self, id: PipelineId) -> bool {
// self.pipeline.id == id ||
// self.children.iter().any(|&SendableChildFrameTree { frame_tree: ref frame_tree, .. }| {
// frame_tree.contains(id)
// })
// }
// }
enum ReplaceResult { enum ReplaceResult {
ReplacedNode(Rc<FrameTree>), ReplacedNode(Rc<FrameTree>),
OriginalNode(Rc<FrameTree>), OriginalNode(Rc<FrameTree>),
@ -511,11 +503,12 @@ impl Constellation {
== subpage_id == subpage_id
}; };
let frames = self.find_all(pipeline_id);
{ {
// Update a child's frame rect and inform its script task of the change, // Update a child's frame rect and inform its script task of the change,
// if it hasn't been already. Optionally inform the compositor if // if it hasn't been already. Optionally inform the compositor if
// resize happens immediately. // resize happens immediately.
let compositor_chan = self.compositor_chan.clone();
let update_child_rect = |child_frame_tree: &mut ChildFrameTree, is_active: bool| { let update_child_rect = |child_frame_tree: &mut ChildFrameTree, is_active: bool| {
child_frame_tree.rect = Some(rect.clone()); child_frame_tree.rect = Some(rect.clone());
// NOTE: work around borrowchk issues // NOTE: work around borrowchk issues
@ -524,21 +517,19 @@ impl Constellation {
let Size2D { width, height } = rect.size; let Size2D { width, height } = rect.size;
if is_active { if is_active {
let pipeline = pipeline.get().borrow(); let pipeline = pipeline.get().borrow();
let ScriptChan(ref chan) = pipeline.script_chan; let ScriptChan(ref script_chan) = pipeline.script_chan;
chan.send(ResizeMsg(pipeline.id, Size2D { script_chan.send(ResizeMsg(pipeline.id, Size2D {
width: width as uint, width: width as uint,
height: height as uint height: height as uint
})); }));
compositor_chan.send(SetLayerClipRect(pipeline.id, rect)); self.compositor_chan.send(SetLayerClipRect(pipeline.id,
LayerId::null(),
rect));
} else { } else {
let pipeline = pipeline.get().borrow(); let pipeline = pipeline.get().borrow();
let ScriptChan(ref chan) = pipeline.script_chan; already_sent.insert(pipeline.id);
chan.send(ResizeInactiveMsg(pipeline.id,
Size2D(width as uint, height as uint)));
} }
let pipeline = pipeline.get().borrow(); };
already_sent.insert(pipeline.id);
}
}; };
// If the subframe is in the current frame tree, the compositor needs the new size // If the subframe is in the current frame tree, the compositor needs the new size
@ -554,7 +545,6 @@ impl Constellation {
} }
// Update all frames with matching pipeline- and subpage-ids // Update all frames with matching pipeline- and subpage-ids
let frames = self.find_all(pipeline_id);
for frame_tree in frames.iter() { for frame_tree in frames.iter() {
// NOTE: work around borrowchk issues // NOTE: work around borrowchk issues
let mut tmp = frame_tree.borrow().children.borrow_mut(); let mut tmp = frame_tree.borrow().children.borrow_mut();

View file

@ -5,7 +5,7 @@
use layout::incremental::RestyleDamage; use layout::incremental::RestyleDamage;
use layout::util::LayoutDataAccess; use layout::util::LayoutDataAccess;
use layout::wrapper::{TLayoutNode, ThreadSafeLayoutNode}; use layout::wrapper::{TLayoutNode, ThreadSafeLayoutNode};
use layout::wrapper::{After, AfterBlock, Before, BeforeBlock, Normal};
use std::cast; use std::cast;
use style::ComputedValues; use style::ComputedValues;
use sync::Arc; use sync::Arc;
@ -25,13 +25,35 @@ impl<'ln> NodeUtil for ThreadSafeLayoutNode<'ln> {
fn get_css_select_results<'a>(&'a self) -> &'a Arc<ComputedValues> { fn get_css_select_results<'a>(&'a self) -> &'a Arc<ComputedValues> {
unsafe { unsafe {
let layout_data_ref = self.borrow_layout_data(); let layout_data_ref = self.borrow_layout_data();
cast::transmute_region(layout_data_ref.get() match self.get_element_type() {
.as_ref() Before | BeforeBlock => {
.unwrap() cast::transmute_region(layout_data_ref.get()
.data .as_ref()
.style .unwrap()
.as_ref() .data
.unwrap()) .before_style
.as_ref()
.unwrap())
}
After | AfterBlock => {
cast::transmute_region(layout_data_ref.get()
.as_ref()
.unwrap()
.data
.after_style
.as_ref()
.unwrap())
}
Normal => {
cast::transmute_region(layout_data_ref.get()
.as_ref()
.unwrap()
.data
.style
.as_ref()
.unwrap())
}
}
} }
} }

File diff suppressed because it is too large Load diff

View file

@ -4,17 +4,29 @@
//! The `Box` type, which represents the leaves of the layout tree. //! The `Box` type, which represents the leaves of the layout tree.
use css::node_style::StyledNode;
use layout::construct::FlowConstructor;
use layout::context::LayoutContext;
use layout::display_list_builder::{DisplayListBuilder, DisplayListBuildingInfo, ToGfxColor};
use layout::floats::{ClearBoth, ClearLeft, ClearRight, ClearType};
use layout::flow::{Flow, FlowFlagsInfo};
use layout::flow;
use layout::model::{Auto, IntrinsicWidths, MaybeAuto, Specified, specified};
use layout::model;
use layout::util::OpaqueNodeMethods;
use layout::wrapper::{TLayoutNode, ThreadSafeLayoutNode};
use extra::url::Url; use extra::url::Url;
use sync::{MutexArc, Arc}; use sync::{MutexArc, Arc};
use geom::{Point2D, Rect, Size2D, SideOffsets2D}; use geom::{Point2D, Rect, Size2D, SideOffsets2D};
use geom::approxeq::ApproxEq; use geom::approxeq::ApproxEq;
use gfx::color::rgb; use gfx::color::rgb;
use gfx::display_list::{BaseDisplayItem, BorderDisplayItem, BorderDisplayItemClass}; use gfx::display_list::{BackgroundAndBorderLevel, BaseDisplayItem, BorderDisplayItem};
use gfx::display_list::{LineDisplayItem, LineDisplayItemClass}; use gfx::display_list::{BorderDisplayItemClass, ClipDisplayItem, ClipDisplayItemClass};
use gfx::display_list::{ImageDisplayItem, ImageDisplayItemClass}; use gfx::display_list::{DisplayList, ImageDisplayItem, ImageDisplayItemClass, LineDisplayItem};
use gfx::display_list::{SolidColorDisplayItem, SolidColorDisplayItemClass, TextDisplayItem}; use gfx::display_list::{LineDisplayItemClass, OpaqueNode, SolidColorDisplayItem};
use gfx::display_list::{TextDisplayItemClass, TextDisplayItemFlags, ClipDisplayItem}; use gfx::display_list::{SolidColorDisplayItemClass, StackingContext, TextDisplayItem};
use gfx::display_list::{ClipDisplayItemClass, DisplayListCollection}; use gfx::display_list::{TextDisplayItemClass, TextDisplayItemFlags};
use gfx::font::FontStyle; use gfx::font::FontStyle;
use gfx::text::text_run::TextRun; use gfx::text::text_run::TextRun;
use servo_msg::constellation_msg::{ConstellationChan, FrameRectMsg, PipelineId, SubpageId}; use servo_msg::constellation_msg::{ConstellationChan, FrameRectMsg, PipelineId, SubpageId};
@ -24,26 +36,16 @@ use servo_util::geometry::Au;
use servo_util::geometry; use servo_util::geometry;
use servo_util::range::*; use servo_util::range::*;
use servo_util::namespace; use servo_util::namespace;
use servo_util::smallvec::{SmallVec, SmallVec0};
use servo_util::str::is_whitespace; use servo_util::str::is_whitespace;
use std::cast; use std::cast;
use std::cell::RefCell; use std::cell::RefCell;
use std::num::Zero; use std::num::Zero;
use style::{ComputedValues, TElement, TNode, cascade, initial_values}; use style::{ComputedValues, TElement, TNode, cascade, initial_values};
use style::computed_values::{LengthOrPercentage, LengthOrPercentageOrAuto, overflow, LPA_Auto}; use style::computed_values::{LengthOrPercentage, LengthOrPercentageOrAuto, overflow, LPA_Auto};
use style::computed_values::{border_style, clear, font_family, line_height, position}; use style::computed_values::{background_attachment, background_repeat, border_style, clear};
use style::computed_values::{text_align, text_decoration, vertical_align, visibility, white_space}; use style::computed_values::{font_family, line_height, position, text_align, text_decoration};
use style::computed_values::{vertical_align, visibility, white_space};
use css::node_style::StyledNode;
use layout::construct::FlowConstructor;
use layout::context::LayoutContext;
use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData, ToGfxColor};
use layout::floats::{ClearBoth, ClearLeft, ClearRight, ClearType};
use layout::flow::{Flow, FlowFlagsInfo};
use layout::flow;
use layout::model::{MaybeAuto, specified, Auto, Specified};
use layout::util::OpaqueNode;
use layout::wrapper::{TLayoutNode, ThreadSafeLayoutNode};
/// Boxes (`struct Box`) are the leaves of the layout tree. They cannot position themselves. In /// Boxes (`struct Box`) are the leaves of the layout tree. They cannot position themselves. In
/// general, boxes do not have a simple correspondence with CSS boxes in the specification: /// general, boxes do not have a simple correspondence with CSS boxes in the specification:
@ -288,18 +290,21 @@ pub enum SplitBoxResult {
} }
/// data for inline boxes /// Data for inline boxes.
///
/// FIXME(#2013, pcwalton): Copying `InlineParentInfo` vectors all the time is really inefficient.
/// Use atomic reference counting instead.
#[deriving(Clone)] #[deriving(Clone)]
pub struct InlineInfo { pub struct InlineInfo {
parent_info: ~[InlineParentInfo], parent_info: SmallVec0<InlineParentInfo>,
baseline: Au, baseline: Au,
} }
impl InlineInfo { impl InlineInfo {
pub fn new() -> InlineInfo { pub fn new() -> InlineInfo {
InlineInfo { InlineInfo {
parent_info: ~[], parent_info: SmallVec0::new(),
baseline: Au::new(0), baseline: Au(0),
} }
} }
} }
@ -414,10 +419,16 @@ def_noncontent_horiz!(left, merge_noncontent_inline_left, clear_noncontent_inl
def_noncontent_horiz!(right, merge_noncontent_inline_right, clear_noncontent_inline_right) def_noncontent_horiz!(right, merge_noncontent_inline_right, clear_noncontent_inline_right)
impl Box { impl Box {
/// Constructs a new `Box` instance. /// Constructs a new `Box` instance for the given node.
///
/// Arguments:
///
/// * `constructor`: The flow constructor.
///
/// * `node`: The node to create a box for.
pub fn new(constructor: &mut FlowConstructor, node: &ThreadSafeLayoutNode) -> Box { pub fn new(constructor: &mut FlowConstructor, node: &ThreadSafeLayoutNode) -> Box {
Box { Box {
node: OpaqueNode::from_thread_safe_layout_node(node), node: OpaqueNodeMethods::from_thread_safe_layout_node(node),
style: node.style().clone(), style: node.style().clone(),
border_box: RefCell::new(Au::zero_rect()), border_box: RefCell::new(Au::zero_rect()),
border: RefCell::new(Zero::zero()), border: RefCell::new(Zero::zero()),
@ -433,7 +444,7 @@ impl Box {
/// Constructs a new `Box` instance from a specific info. /// Constructs a new `Box` instance from a specific info.
pub fn new_from_specific_info(node: &ThreadSafeLayoutNode, specific: SpecificBoxInfo) -> Box { pub fn new_from_specific_info(node: &ThreadSafeLayoutNode, specific: SpecificBoxInfo) -> Box {
Box { Box {
node: OpaqueNode::from_thread_safe_layout_node(node), node: OpaqueNodeMethods::from_thread_safe_layout_node(node),
style: node.style().clone(), style: node.style().clone(),
border_box: RefCell::new(Au::zero_rect()), border_box: RefCell::new(Au::zero_rect()),
border: RefCell::new(Zero::zero()), border: RefCell::new(Zero::zero()),
@ -460,7 +471,7 @@ impl Box {
let (node_style, _) = cascade(&[], false, Some(node.style().get()), let (node_style, _) = cascade(&[], false, Some(node.style().get()),
&initial_values(), None); &initial_values(), None);
Box { Box {
node: OpaqueNode::from_thread_safe_layout_node(node), node: OpaqueNodeMethods::from_thread_safe_layout_node(node),
style: Arc::new(node_style), style: Arc::new(node_style),
border_box: RefCell::new(Au::zero_rect()), border_box: RefCell::new(Au::zero_rect()),
border: RefCell::new(Zero::zero()), border: RefCell::new(Zero::zero()),
@ -589,54 +600,55 @@ impl Box {
} }
} }
/// Returns the shared part of the width for computation of minimum and preferred width per /// Uses the style only to estimate the intrinsic widths. These may be modified for text or
/// CSS 2.1. /// replaced elements.
fn guess_width(&self) -> Au { fn style_specified_intrinsic_width(&self) -> IntrinsicWidths {
let (use_margins, use_padding) = match self.specific {
GenericBox | IframeBox(_) | ImageBox(_) => (true, true),
TableBox | TableCellBox => (false, true),
TableWrapperBox => (true, false),
TableRowBox => (false, false),
ScannedTextBox(_) | TableColumnBox(_) | UnscannedTextBox(_) => {
// Styles are irrelevant for these kinds of boxes.
return IntrinsicWidths::new()
}
};
let style = self.style(); let style = self.style();
let mut margin_left = Au::new(0);
let mut margin_right = Au::new(0);
let mut padding_left = Au::new(0);
let mut padding_right = Au::new(0);
match self.specific {
GenericBox | IframeBox(_) | ImageBox(_) => {
margin_left = MaybeAuto::from_style(style.Margin.get().margin_left,
Au::new(0)).specified_or_zero();
margin_right = MaybeAuto::from_style(style.Margin.get().margin_right,
Au::new(0)).specified_or_zero();
padding_left = self.compute_padding_length(style.Padding.get().padding_left,
Au::new(0));
padding_right = self.compute_padding_length(style.Padding.get().padding_right,
Au::new(0));
}
TableBox | TableCellBox => {
padding_left = self.compute_padding_length(style.Padding.get().padding_left,
Au::new(0));
padding_right = self.compute_padding_length(style.Padding.get().padding_right,
Au::new(0));
}
TableWrapperBox => {
margin_left = MaybeAuto::from_style(style.Margin.get().margin_left,
Au::new(0)).specified_or_zero();
margin_right = MaybeAuto::from_style(style.Margin.get().margin_right,
Au::new(0)).specified_or_zero();
}
TableRowBox => {}
ScannedTextBox(_) | TableColumnBox(_) | UnscannedTextBox(_) => return Au(0),
}
let width = MaybeAuto::from_style(style.Box.get().width, Au::new(0)).specified_or_zero(); let width = MaybeAuto::from_style(style.Box.get().width, Au::new(0)).specified_or_zero();
width + margin_left + margin_right + padding_left + padding_right + let (margin_left, margin_right) = if use_margins {
self.border.get().left + self.border.get().right (MaybeAuto::from_style(style.Margin.get().margin_left, Au(0)).specified_or_zero(),
MaybeAuto::from_style(style.Margin.get().margin_right, Au(0)).specified_or_zero())
} else {
(Au(0), Au(0))
};
let (padding_left, padding_right) = if use_padding {
(self.compute_padding_length(style.Padding.get().padding_left, Au(0)),
self.compute_padding_length(style.Padding.get().padding_right, Au(0)))
} else {
(Au(0), Au(0))
};
let surround_width = margin_left + margin_right + padding_left + padding_right +
self.border.get().left + self.border.get().right;
IntrinsicWidths {
minimum_width: width,
preferred_width: width,
surround_width: surround_width,
}
} }
pub fn calculate_line_height(&self, font_size: Au) -> Au { pub fn calculate_line_height(&self, font_size: Au) -> Au {
match self.line_height() { let from_inline = match self.style().InheritedBox.get().line_height {
line_height::Normal => font_size.scale_by(1.14), line_height::Normal => font_size.scale_by(1.14),
line_height::Number(l) => font_size.scale_by(l), line_height::Number(l) => font_size.scale_by(l),
line_height::Length(l) => l line_height::Length(l) => l
} };
let minimum = self.style().InheritedBox.get()._servo_minimum_line_height;
Au::max(from_inline, minimum)
} }
/// Populates the box model border parameters from the given computed style. /// Populates the box model border parameters from the given computed style.
@ -864,10 +876,6 @@ impl Box {
self.style().InheritedText.get().text_align self.style().InheritedText.get().text_align
} }
pub fn line_height(&self) -> line_height::T {
self.style().InheritedBox.get().line_height
}
pub fn vertical_align(&self) -> vertical_align::T { pub fn vertical_align(&self) -> vertical_align::T {
self.style().Box.get().vertical_align self.style().Box.get().vertical_align
} }
@ -920,15 +928,6 @@ impl Box {
} }
} }
/// Returns true if this element is replaced content. This is true for images, form elements,
/// and so on.
pub fn is_replaced(&self) -> bool {
match self.specific {
ImageBox(..) => true,
_ => false,
}
}
/// Returns true if this element can be split. This is true for text boxes. /// Returns true if this element can be split. This is true for text boxes.
pub fn can_split(&self) -> bool { pub fn can_split(&self) -> bool {
match self.specific { match self.specific {
@ -951,12 +950,10 @@ impl Box {
(Au::new(0), Au::new(0)) (Au::new(0), Au::new(0))
} }
pub fn paint_inline_background_border_if_applicable<E:ExtraDisplayListData>( pub fn paint_inline_background_border_if_applicable(&self,
&self, list: &mut DisplayList,
index: uint, absolute_bounds: &Rect<Au>,
lists: &RefCell<DisplayListCollection<E>>, offset: &Point2D<Au>) {
absolute_bounds: &Rect<Au>,
offset: &Point2D<Au>) {
// FIXME: This causes a lot of background colors to be displayed when they are clearly not // FIXME: This causes a lot of background colors to be displayed when they are clearly not
// needed. We could use display list optimization to clean this up, but it still seems // needed. We could use display list optimization to clean this up, but it still seems
// inefficient. What we really want is something like "nearest ancestor element that // inefficient. What we really want is something like "nearest ancestor element that
@ -965,7 +962,7 @@ impl Box {
match info.get() { match info.get() {
&Some(ref box_info) => { &Some(ref box_info) => {
let mut bg_rect = absolute_bounds.clone(); let mut bg_rect = absolute_bounds.clone();
for info in box_info.parent_info.rev_iter() { for info in box_info.parent_info.as_slice().rev_iter() {
// TODO (ksh8281) compute vertical-align, line-height // TODO (ksh8281) compute vertical-align, line-height
bg_rect.origin.y = box_info.baseline + offset.y - info.font_ascent; bg_rect.origin.y = box_info.baseline + offset.y - info.font_ascent;
bg_rect.size.height = info.font_ascent + info.font_descent; bg_rect.size.height = info.font_ascent + info.font_descent;
@ -973,23 +970,24 @@ impl Box {
info.style.get().Background.get().background_color); info.style.get().Background.get().background_color);
if !background_color.alpha.approx_eq(&0.0) { if !background_color.alpha.approx_eq(&0.0) {
lists.with_mut(|lists| { let solid_color_display_item = ~SolidColorDisplayItem {
let solid_color_display_item = ~SolidColorDisplayItem { base: BaseDisplayItem {
base: BaseDisplayItem { bounds: bg_rect.clone(),
bounds: bg_rect.clone(), node: self.node,
extra: ExtraDisplayListData::new(self), },
}, color: background_color.to_gfx_color(),
color: background_color.to_gfx_color(), };
};
lists.lists[index].append_item(SolidColorDisplayItemClass(solid_color_display_item)) list.push(SolidColorDisplayItemClass(solid_color_display_item))
});
} }
let border = &info.border; let border = &info.border;
// Fast path. // Fast path.
if border.is_zero() { if border.is_zero() {
continue; continue
} }
bg_rect.origin.y = bg_rect.origin.y - border.top; bg_rect.origin.y = bg_rect.origin.y - border.top;
bg_rect.size.height = bg_rect.size.height + border.top + border.bottom; bg_rect.size.height = bg_rect.size.height + border.top + border.bottom;
@ -1003,26 +1001,20 @@ impl Box {
let bottom_style = style.Border.get().border_bottom_style; let bottom_style = style.Border.get().border_bottom_style;
let left_style = style.Border.get().border_left_style; let left_style = style.Border.get().border_left_style;
let border_display_item = ~BorderDisplayItem {
base: BaseDisplayItem {
bounds: bg_rect,
node: self.node,
},
border: border.clone(),
color: SideOffsets2D::new(top_color.to_gfx_color(),
right_color.to_gfx_color(),
bottom_color.to_gfx_color(),
left_color.to_gfx_color()),
style: SideOffsets2D::new(top_style, right_style, bottom_style, left_style)
};
lists.with_mut(|lists| { list.push(BorderDisplayItemClass(border_display_item));
let border_display_item = ~BorderDisplayItem {
base: BaseDisplayItem {
bounds: bg_rect,
extra: ExtraDisplayListData::new(self),
},
border: border.clone(),
color: SideOffsets2D::new(top_color.to_gfx_color(),
right_color.to_gfx_color(),
bottom_color.to_gfx_color(),
left_color.to_gfx_color()),
style: SideOffsets2D::new(top_style,
right_style,
bottom_style,
left_style)
};
lists.lists[index].append_item(BorderDisplayItemClass(border_display_item))
});
bg_rect.origin.x = bg_rect.origin.x + border.left; bg_rect.origin.x = bg_rect.origin.x + border.left;
bg_rect.size.width = bg_rect.size.width - border.left - border.right; bg_rect.size.width = bg_rect.size.width - border.left - border.right;
@ -1033,11 +1025,9 @@ impl Box {
} }
/// Adds the display items necessary to paint the background of this box to the display list if /// Adds the display items necessary to paint the background of this box to the display list if
/// necessary. /// necessary.
pub fn paint_background_if_applicable<E:ExtraDisplayListData>( pub fn paint_background_if_applicable(&self,
&self, list: &mut DisplayList,
builder: &DisplayListBuilder, builder: &DisplayListBuilder,
index: uint,
lists: &RefCell<DisplayListCollection<E>>,
absolute_bounds: &Rect<Au>) { absolute_bounds: &Rect<Au>) {
// FIXME: This causes a lot of background colors to be displayed when they are clearly not // FIXME: This causes a lot of background colors to be displayed when they are clearly not
// needed. We could use display list optimization to clean this up, but it still seems // needed. We could use display list optimization to clean this up, but it still seems
@ -1046,17 +1036,15 @@ impl Box {
let style = self.style(); let style = self.style();
let background_color = style.resolve_color(style.Background.get().background_color); let background_color = style.resolve_color(style.Background.get().background_color);
if !background_color.alpha.approx_eq(&0.0) { if !background_color.alpha.approx_eq(&0.0) {
lists.with_mut(|lists| { let display_item = ~SolidColorDisplayItem {
let solid_color_display_item = ~SolidColorDisplayItem { base: BaseDisplayItem {
base: BaseDisplayItem { bounds: *absolute_bounds,
bounds: *absolute_bounds, node: self.node,
extra: ExtraDisplayListData::new(self), },
}, color: background_color.to_gfx_color(),
color: background_color.to_gfx_color(), };
};
lists.lists[index].append_item(SolidColorDisplayItemClass(solid_color_display_item)) list.push(SolidColorDisplayItemClass(display_item))
});
} }
// The background image is painted on top of the background color. // The background image is painted on top of the background color.
@ -1064,22 +1052,81 @@ impl Box {
// http://www.w3.org/TR/CSS21/colors.html#background // http://www.w3.org/TR/CSS21/colors.html#background
match style.Background.get().background_image { match style.Background.get().background_image {
Some(ref image_url) => { Some(ref image_url) => {
let mut holder = ImageHolder::new(image_url.clone(), builder.ctx.image_cache.clone()); let mut holder = ImageHolder::new(image_url.clone(),
builder.ctx.image_cache.clone());
match holder.get_image() { match holder.get_image() {
Some(image) => { Some(image) => {
debug!("(building display list) building background image"); debug!("(building display list) building background image");
// Place the image into the display list. // Adjust bounds for `background-position` and `background-attachment`.
lists.with_mut(|lists| { let mut bounds = *absolute_bounds;
let image_display_item = ~ImageDisplayItem { let horizontal_position = model::specified(
base: BaseDisplayItem { style.Background.get().background_position.horizontal,
bounds: *absolute_bounds, bounds.size.width);
extra: ExtraDisplayListData::new(self), let vertical_position = model::specified(
}, style.Background.get().background_position.vertical,
image: image.clone(), bounds.size.height);
};
lists.lists[index].append_item(ImageDisplayItemClass(image_display_item)); let clip_display_item;
match style.Background.get().background_attachment {
background_attachment::scroll => {
clip_display_item = None;
bounds.origin.x = bounds.origin.x + horizontal_position;
bounds.origin.y = bounds.origin.y + vertical_position;
bounds.size.width = bounds.size.width - horizontal_position;
bounds.size.height = bounds.size.height - vertical_position;
}
background_attachment::fixed => {
clip_display_item = Some(~ClipDisplayItem {
base: BaseDisplayItem {
bounds: bounds,
node: self.node,
},
child_list: SmallVec0::new(),
need_clip: true,
});
bounds = Rect {
origin: Point2D(horizontal_position, vertical_position),
size: Size2D(bounds.origin.x + bounds.size.width,
bounds.origin.y + bounds.size.height),
}
}
}
// Adjust sizes for `background-repeat`.
match style.Background.get().background_repeat {
background_repeat::no_repeat => {
bounds.size.width = Au::from_px(image.get().width as int);
bounds.size.height = Au::from_px(image.get().height as int)
}
background_repeat::repeat_x => {
bounds.size.height = Au::from_px(image.get().height as int)
}
background_repeat::repeat_y => {
bounds.size.width = Au::from_px(image.get().width as int)
}
background_repeat::repeat => {}
};
// Create the image display item.
let image_display_item = ImageDisplayItemClass(~ImageDisplayItem {
base: BaseDisplayItem {
bounds: bounds,
node: self.node,
},
image: image.clone(),
stretch_size: Size2D(Au::from_px(image.get().width as int),
Au::from_px(image.get().height as int)),
}); });
match clip_display_item {
None => list.push(image_display_item),
Some(mut clip_display_item) => {
clip_display_item.child_list.push(image_display_item);
list.push(ClipDisplayItemClass(clip_display_item))
}
}
} }
None => { None => {
// No image data at all? Do nothing. // No image data at all? Do nothing.
@ -1095,11 +1142,7 @@ impl Box {
/// Adds the display items necessary to paint the borders of this box to a display list if /// Adds the display items necessary to paint the borders of this box to a display list if
/// necessary. /// necessary.
pub fn paint_borders_if_applicable<E:ExtraDisplayListData>( pub fn paint_borders_if_applicable(&self, list: &mut DisplayList, abs_bounds: &Rect<Au>) {
&self,
index: uint,
lists: &RefCell<DisplayListCollection<E>>,
abs_bounds: &Rect<Au>) {
// Fast path. // Fast path.
let border = self.border.get(); let border = self.border.get();
if border.is_zero() { if border.is_zero() {
@ -1122,75 +1165,142 @@ impl Box {
- self.noncontent_inline_right(); - self.noncontent_inline_right();
// Append the border to the display list. // Append the border to the display list.
lists.with_mut(|lists| { let border_display_item = ~BorderDisplayItem {
let border_display_item = ~BorderDisplayItem { base: BaseDisplayItem {
base: BaseDisplayItem { bounds: abs_bounds,
bounds: abs_bounds, node: self.node,
extra: ExtraDisplayListData::new(self), },
}, border: border,
border: border, color: SideOffsets2D::new(top_color.to_gfx_color(),
color: SideOffsets2D::new(top_color.to_gfx_color(), right_color.to_gfx_color(),
right_color.to_gfx_color(), bottom_color.to_gfx_color(),
bottom_color.to_gfx_color(), left_color.to_gfx_color()),
left_color.to_gfx_color()), style: SideOffsets2D::new(top_style,
style: SideOffsets2D::new(top_style, right_style,
right_style, bottom_style,
bottom_style, left_style)
left_style) };
};
lists.lists[index].append_item(BorderDisplayItemClass(border_display_item)) list.push(BorderDisplayItemClass(border_display_item))
});
} }
/// Adds the display items for this box to the given display list. fn build_debug_borders_around_text_boxes(&self,
stacking_context: &mut StackingContext,
flow_origin: Point2D<Au>,
text_box: &ScannedTextBoxInfo) {
let box_bounds = self.border_box.get();
let absolute_box_bounds = box_bounds.translate(&flow_origin);
// Compute the text box bounds and draw a border surrounding them.
let debug_border = SideOffsets2D::new_all_same(Au::from_px(1));
let border_display_item = ~BorderDisplayItem {
base: BaseDisplayItem {
bounds: absolute_box_bounds,
node: self.node,
},
border: debug_border,
color: SideOffsets2D::new_all_same(rgb(0, 0, 200)),
style: SideOffsets2D::new_all_same(border_style::solid)
};
stacking_context.content.push(BorderDisplayItemClass(border_display_item));
// Draw a rectangle representing the baselines.
let ascent = text_box.run.get().metrics_for_range(&text_box.range).ascent;
let baseline = Rect(absolute_box_bounds.origin + Point2D(Au(0), ascent),
Size2D(absolute_box_bounds.size.width, Au(0)));
let line_display_item = ~LineDisplayItem {
base: BaseDisplayItem {
bounds: baseline,
node: self.node,
},
color: rgb(0, 200, 0),
style: border_style::dashed,
};
stacking_context.content.push(LineDisplayItemClass(line_display_item))
}
fn build_debug_borders_around_box(&self,
stacking_context: &mut StackingContext,
flow_origin: Point2D<Au>) {
let box_bounds = self.border_box.get();
let absolute_box_bounds = box_bounds.translate(&flow_origin);
// This prints a debug border around the border of this box.
let debug_border = SideOffsets2D::new_all_same(Au::from_px(1));
let border_display_item = ~BorderDisplayItem {
base: BaseDisplayItem {
bounds: absolute_box_bounds,
node: self.node,
},
border: debug_border,
color: SideOffsets2D::new_all_same(rgb(0, 0, 200)),
style: SideOffsets2D::new_all_same(border_style::solid)
};
stacking_context.content.push(BorderDisplayItemClass(border_display_item))
}
/// Adds the display items for this box to the given stacking context.
/// ///
/// Arguments: /// Arguments:
///
/// * `stacking_context`: The stacking context to add display items to.
/// * `builder`: The display list builder, which manages the coordinate system and options. /// * `builder`: The display list builder, which manages the coordinate system and options.
/// * `dirty`: The dirty rectangle in the coordinate system of the owning flow. /// * `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. /// * `flow_origin`: Position of the origin of the owning flow wrt the display list root flow.
/// box. /// box.
/// * `list`: The display list to which items should be appended. /// * `flow`: The flow that this box belongs to.
/// pub fn build_display_list(&self,
/// TODO: To implement stacking contexts correctly, we need to create a set of display lists, stacking_context: &mut StackingContext,
/// one per layer of the stacking context (CSS 2.1 § 9.9.1). Each box is passed the list set
/// representing the box's stacking context. When asked to construct its constituent display
/// items, each box puts its display items into the correct stack layer according to CSS 2.1
/// Appendix E. Finally, the builder flattens the list.
pub fn build_display_list<E:ExtraDisplayListData>(
&self,
builder: &DisplayListBuilder, builder: &DisplayListBuilder,
dirty: &Rect<Au>, _: &DisplayListBuildingInfo,
flow_origin: Point2D<Au>, flow_origin: Point2D<Au>,
flow: &Flow, flow: &Flow,
index: uint, background_and_border_level: BackgroundAndBorderLevel) {
lists: &RefCell<DisplayListCollection<E>>) {
// Box position wrt to the owning flow. // Box position wrt to the owning flow.
let box_bounds = self.border_box.get(); let box_bounds = self.border_box.get();
let absolute_box_bounds = box_bounds.translate(&flow_origin); let absolute_box_bounds = box_bounds.translate(&flow_origin);
debug!("Box::build_display_list at rel={}, abs={}: {:s}", debug!("Box::build_display_list at rel={}, abs={}: {:s}",
box_bounds, absolute_box_bounds, self.debug_str()); box_bounds,
debug!("Box::build_display_list: dirty={}, flow_origin={}", *dirty, flow_origin); absolute_box_bounds,
self.debug_str());
debug!("Box::build_display_list: dirty={}, flow_origin={}", builder.dirty, flow_origin);
if self.style().InheritedBox.get().visibility != visibility::visible { if self.style().InheritedBox.get().visibility != visibility::visible {
return; return
} }
if absolute_box_bounds.intersects(dirty) { if !absolute_box_bounds.intersects(&builder.dirty) {
debug!("Box::build_display_list: intersected. Adding display item...");
} else {
debug!("Box::build_display_list: Did not intersect..."); debug!("Box::build_display_list: Did not intersect...");
return; return
} }
self.paint_inline_background_border_if_applicable(index, lists, &absolute_box_bounds, &flow_origin); debug!("Box::build_display_list: intersected. Adding display item...");
// Add the background to the list, if applicable.
self.paint_background_if_applicable(builder, index, lists, &absolute_box_bounds);
// Add a border, if applicable. {
// let list =
// TODO: Outlines. stacking_context.list_for_background_and_border_level(background_and_border_level);
self.paint_borders_if_applicable(index, lists, &absolute_box_bounds);
// Add a background to the list, if this is an inline.
//
// FIXME(pcwalton): This is kind of ugly; merge with the call below?
self.paint_inline_background_border_if_applicable(list,
&absolute_box_bounds,
&flow_origin);
// Add the background to the list, if applicable.
self.paint_background_if_applicable(list, builder, &absolute_box_bounds);
// Add a border, if applicable.
//
// TODO: Outlines.
self.paint_borders_if_applicable(list, &absolute_box_bounds);
}
match self.specific { match self.specific {
UnscannedTextBox(_) => fail!("Shouldn't see unscanned boxes here."), UnscannedTextBox(_) => fail!("Shouldn't see unscanned boxes here."),
@ -1204,7 +1314,7 @@ impl Box {
let inline_info = self.inline_info.borrow(); let inline_info = self.inline_info.borrow();
match inline_info.get() { match inline_info.get() {
&Some(ref info) => { &Some(ref info) => {
for data in info.parent_info.rev_iter() { for data in info.parent_info.as_slice().rev_iter() {
let parent_info = FlowFlagsInfo::new(data.style.get()); let parent_info = FlowFlagsInfo::new(data.style.get());
flow_flags.propagate_text_decoration_from_parent(&parent_info); flow_flags.propagate_text_decoration_from_parent(&parent_info);
} }
@ -1224,100 +1334,45 @@ impl Box {
- self.noncontent_inline_right(); - self.noncontent_inline_right();
// Create the text box. // Create the text box.
lists.with_mut(|lists| { let text_display_item = ~TextDisplayItem {
let text_display_item = ~TextDisplayItem { base: BaseDisplayItem {
base: BaseDisplayItem { bounds: bounds,
bounds: bounds, node: self.node,
extra: ExtraDisplayListData::new(self), },
}, text_run: text_box.run.clone(),
text_run: text_box.run.clone(), range: text_box.range,
range: text_box.range, text_color: text_color,
text_color: text_color, overline_color: flow_flags.overline_color(text_color),
overline_color: flow_flags.overline_color(text_color), underline_color: flow_flags.underline_color(text_color),
underline_color: flow_flags.underline_color(text_color), line_through_color: flow_flags.line_through_color(text_color),
line_through_color: flow_flags.line_through_color(text_color), flags: text_flags,
flags: text_flags, };
};
lists.lists[index].append_item(TextDisplayItemClass(text_display_item)); stacking_context.content.push(TextDisplayItemClass(text_display_item));
});
// Draw debug frames for text bounds. // Draw debug frames for text bounds.
// //
// FIXME(pcwalton): This is a bit of an abuse of the logging infrastructure. We // FIXME(pcwalton): This is a bit of an abuse of the logging infrastructure. We
// should have a real `SERVO_DEBUG` system. // should have a real `SERVO_DEBUG` system.
debug!("{:?}", { debug!("{:?}", self.build_debug_borders_around_text_boxes(stacking_context,
// Compute the text box bounds and draw a border surrounding them. flow_origin,
let debug_border = SideOffsets2D::new_all_same(Au::from_px(1)); text_box))
lists.with_mut(|lists| {
let border_display_item = ~BorderDisplayItem {
base: BaseDisplayItem {
bounds: absolute_box_bounds,
extra: ExtraDisplayListData::new(self),
},
border: debug_border,
color: SideOffsets2D::new_all_same(rgb(0, 0, 200)),
style: SideOffsets2D::new_all_same(border_style::solid)
};
lists.lists[index].append_item(BorderDisplayItemClass(border_display_item));
});
// Draw a rectangle representing the baselines.
let ascent = text_box.run.get().metrics_for_range(
&text_box.range).ascent;
let baseline = Rect(absolute_box_bounds.origin + Point2D(Au(0), ascent),
Size2D(absolute_box_bounds.size.width, Au(0)));
lists.with_mut(|lists| {
let line_display_item = ~LineDisplayItem {
base: BaseDisplayItem {
bounds: baseline,
extra: ExtraDisplayListData::new(self),
},
color: rgb(0, 200, 0),
style: border_style::dashed
};
lists.lists[index].append_item(LineDisplayItemClass(line_display_item));
});
});
}, },
GenericBox | IframeBox(..) | TableBox | TableCellBox | TableRowBox | GenericBox | IframeBox(..) | TableBox | TableCellBox | TableRowBox |
TableWrapperBox => { TableWrapperBox => {
lists.with_mut(|lists| { let item = ~ClipDisplayItem {
let item = ~ClipDisplayItem { base: BaseDisplayItem {
base: BaseDisplayItem { bounds: absolute_box_bounds,
bounds: absolute_box_bounds, node: self.node,
extra: ExtraDisplayListData::new(self), },
}, child_list: SmallVec0::new(),
child_list: ~[], need_clip: self.needs_clip()
need_clip: self.needs_clip() };
}; stacking_context.content.push(ClipDisplayItemClass(item));
lists.lists[index].append_item(ClipDisplayItemClass(item));
});
// FIXME(pcwalton): This is a bit of an abuse of the logging infrastructure. We // FIXME(pcwalton): This is a bit of an abuse of the logging infrastructure. We
// should have a real `SERVO_DEBUG` system. // should have a real `SERVO_DEBUG` system.
debug!("{:?}", { debug!("{:?}", self.build_debug_borders_around_box(stacking_context, flow_origin))
// This prints a debug border around the border of this box.
let debug_border = SideOffsets2D::new_all_same(Au::from_px(1));
lists.with_mut(|lists| {
let border_display_item = ~BorderDisplayItem {
base: BaseDisplayItem {
bounds: absolute_box_bounds,
extra: ExtraDisplayListData::new(self),
},
border: debug_border,
color: SideOffsets2D::new_all_same(rgb(0, 0, 200)),
style: SideOffsets2D::new_all_same(border_style::solid)
};
lists.lists[index].append_item(BorderDisplayItemClass(border_display_item));
});
});
}, },
ImageBox(ref image_box) => { ImageBox(ref image_box) => {
let mut image_ref = image_box.image.borrow_mut(); let mut image_ref = image_box.image.borrow_mut();
@ -1335,16 +1390,15 @@ impl Box {
debug!("(building display list) building image box"); debug!("(building display list) building image box");
// Place the image into the display list. // Place the image into the display list.
lists.with_mut(|lists| { let image_display_item = ~ImageDisplayItem {
let image_display_item = ~ImageDisplayItem { base: BaseDisplayItem {
base: BaseDisplayItem { bounds: bounds,
bounds: bounds, node: self.node,
extra: ExtraDisplayListData::new(self), },
}, image: image.clone(),
image: image.clone(), stretch_size: bounds.size,
}; };
lists.lists[index].append_item(ImageDisplayItemClass(image_display_item)); stacking_context.content.push(ImageDisplayItemClass(image_display_item))
});
} }
None => { None => {
// No image data at all? Do nothing. // No image data at all? Do nothing.
@ -1355,24 +1409,7 @@ impl Box {
} }
// FIXME(pcwalton): This is a bit of an abuse of the logging infrastructure. We // FIXME(pcwalton): This is a bit of an abuse of the logging infrastructure. We
// should have a real `SERVO_DEBUG` system. // should have a real `SERVO_DEBUG` system.
debug!("{:?}", { debug!("{:?}", self.build_debug_borders_around_box(stacking_context, flow_origin))
let debug_border = SideOffsets2D::new_all_same(Au::from_px(1));
lists.with_mut(|lists| {
let border_display_item = ~BorderDisplayItem {
base: BaseDisplayItem {
bounds: absolute_box_bounds,
extra: ExtraDisplayListData::new(self),
},
border: debug_border,
color: SideOffsets2D::new_all_same(rgb(0, 0, 200)),
style: SideOffsets2D::new_all_same(border_style::solid)
};
lists.lists[index].append_item(BorderDisplayItemClass(border_display_item))
});
});
} }
} }
@ -1392,18 +1429,19 @@ impl Box {
} }
_ => {} _ => {}
} }
} }
/// Returns the *minimum width* and *preferred width* of this box as defined by CSS 2.1. /// Returns the intrinsic widths of this fragment.
pub fn minimum_and_preferred_widths(&self) -> (Au, Au) { pub fn intrinsic_widths(&self) -> IntrinsicWidths {
let guessed_width = self.guess_width(); let mut result = self.style_specified_intrinsic_width();
let (additional_minimum, additional_preferred) = match self.specific {
GenericBox | IframeBox(_) | TableBox | TableCellBox | TableColumnBox(_) | match self.specific {
TableRowBox | TableWrapperBox => (Au(0), Au(0)), GenericBox | IframeBox(_) | TableBox | TableCellBox | TableColumnBox(_) | TableRowBox |
TableWrapperBox => {}
ImageBox(ref image_box_info) => { ImageBox(ref image_box_info) => {
let image_width = image_box_info.image_width(); let image_width = image_box_info.image_width();
(image_width, image_width) result.minimum_width = geometry::max(result.minimum_width, image_width);
result.preferred_width = geometry::max(result.preferred_width, image_width);
} }
ScannedTextBox(ref text_box_info) => { ScannedTextBox(ref text_box_info) => {
let range = &text_box_info.range; let range = &text_box_info.range;
@ -1415,11 +1453,29 @@ impl Box {
max_line_width = Au::max(max_line_width, line_metrics.advance_width); max_line_width = Au::max(max_line_width, line_metrics.advance_width);
} }
(min_line_width, max_line_width) result.minimum_width = geometry::max(result.minimum_width, min_line_width);
result.preferred_width = geometry::max(result.preferred_width, max_line_width);
} }
UnscannedTextBox(..) => fail!("Unscanned text boxes should have been scanned by now!"), UnscannedTextBox(..) => fail!("Unscanned text boxes should have been scanned by now!"),
}; }
(guessed_width + additional_minimum, guessed_width + additional_preferred)
// Take borders and padding for parent inline boxes into account.
let inline_info = self.inline_info.get();
match inline_info {
None => {}
Some(ref inline_info) => {
for inline_parent_info in inline_info.parent_info.iter() {
let border_width = inline_parent_info.border.left +
inline_parent_info.border.right;
let padding_width = inline_parent_info.padding.left +
inline_parent_info.padding.right;
result.minimum_width = result.minimum_width + border_width + padding_width;
result.preferred_width = result.preferred_width + border_width + padding_width;
}
}
}
result
} }
@ -1440,8 +1496,8 @@ impl Box {
UnscannedTextBox(_) => fail!("Unscanned text boxes should have been scanned by now!"), UnscannedTextBox(_) => fail!("Unscanned text boxes should have been scanned by now!"),
} }
} }
/// Returns, and computes, the height of this box. /// Returns, and computes, the height of this box.
///
pub fn content_height(&self) -> Au { pub fn content_height(&self) -> Au {
match self.specific { match self.specific {
GenericBox | IframeBox(_) | TableBox | TableCellBox | TableRowBox | GenericBox | IframeBox(_) | TableBox | TableCellBox | TableRowBox |
@ -1625,16 +1681,16 @@ impl Box {
} }
} }
/// Assigns replaced width for this box only if it is replaced content. /// Assigns replaced width, padding, and margins for this box only if it is replaced content
/// /// per CSS 2.1 § 10.3.2.
/// This assigns only the width, not margin or anything else. pub fn assign_replaced_width_if_necessary(&self, container_width: Au) {
/// CSS 2.1 § 10.3.2.
pub fn assign_replaced_width_if_necessary(&self,container_width: Au) {
match self.specific { match self.specific {
GenericBox | IframeBox(_) | TableBox | TableCellBox | TableRowBox | GenericBox | IframeBox(_) | TableBox | TableCellBox | TableRowBox |
TableWrapperBox => {} TableWrapperBox => {}
ImageBox(ref image_box_info) => { ImageBox(ref image_box_info) => {
// TODO(ksh8281): compute border,margin,padding self.compute_padding(self.style(), container_width);
// TODO(ksh8281): compute border,margin
let width = ImageBoxInfo::style_length(self.style().Box.get().width, let width = ImageBoxInfo::style_length(self.style().Box.get().width,
image_box_info.dom_width, image_box_info.dom_width,
container_width); container_width);

View file

@ -22,14 +22,14 @@
use css::node_style::StyledNode; use css::node_style::StyledNode;
use layout::block::BlockFlow; use layout::block::BlockFlow;
use layout::box_::{Box, GenericBox, IframeBox, IframeBoxInfo, ImageBox, ImageBoxInfo, TableBox}; use layout::box_::{Box, GenericBox, IframeBox, IframeBoxInfo, ImageBox, ImageBoxInfo};
use layout::box_::{TableCellBox, TableColumnBox, TableColumnBoxInfo, TableRowBox, TableWrapperBox}; use layout::box_::{InlineInfo, InlineParentInfo, SpecificBoxInfo};
use layout::box_::{InlineInfo, InlineParentInfo, SpecificBoxInfo, UnscannedTextBox}; use layout::box_::{TableBox, TableCellBox, TableColumnBox, TableColumnBoxInfo, TableRowBox};
use layout::box_::{UnscannedTextBoxInfo}; use layout::box_::{TableWrapperBox, UnscannedTextBox, UnscannedTextBoxInfo};
use layout::context::LayoutContext; use layout::context::LayoutContext;
use layout::floats::FloatKind; use layout::floats::FloatKind;
use layout::flow::{Flow, ImmutableFlowUtils, MutableOwnedFlowUtils}; use layout::flow::{Flow, ImmutableFlowUtils, MutableOwnedFlowUtils};
use layout::flow::{Descendants, AbsDescendants, FixedDescendants}; use layout::flow::{Descendants, AbsDescendants};
use layout::flow_list::{Rawlink}; use layout::flow_list::{Rawlink};
use layout::inline::InlineFlow; use layout::inline::InlineFlow;
use layout::table_wrapper::TableWrapperFlow; use layout::table_wrapper::TableWrapperFlow;
@ -40,31 +40,34 @@ use layout::table_rowgroup::TableRowGroupFlow;
use layout::table_row::TableRowFlow; use layout::table_row::TableRowFlow;
use layout::table_cell::TableCellFlow; use layout::table_cell::TableCellFlow;
use layout::text::TextRunScanner; use layout::text::TextRunScanner;
use layout::util::{LayoutDataAccess, OpaqueNode}; use layout::util::{LayoutDataAccess, OpaqueNodeMethods};
use layout::wrapper::{PostorderNodeMutTraversal, TLayoutNode, ThreadSafeLayoutNode}; use layout::wrapper::{PostorderNodeMutTraversal, TLayoutNode, ThreadSafeLayoutNode};
use layout::wrapper::{Before, BeforeBlock, After, AfterBlock, Normal};
use extra::url::Url;
use gfx::display_list::OpaqueNode;
use gfx::font_context::FontContext; use gfx::font_context::FontContext;
use script::dom::bindings::codegen::InheritTypes::TextCast; use script::dom::bindings::codegen::InheritTypes::TextCast;
use script::dom::bindings::js::JS; use script::dom::bindings::js::JS;
use script::dom::element::{HTMLIFrameElementTypeId, HTMLImageElementTypeId, HTMLObjectElementTypeId}; use script::dom::element::{HTMLIFrameElementTypeId, HTMLImageElementTypeId};
use script::dom::element::{HTMLTableElementTypeId, HTMLTableSectionElementTypeId}; use script::dom::element::{HTMLObjectElementTypeId};
use script::dom::element::{HTMLTableDataCellElementTypeId, HTMLTableHeaderCellElementTypeId}; use script::dom::element::{HTMLTableColElementTypeId, HTMLTableDataCellElementTypeId};
use script::dom::element::{HTMLTableColElementTypeId, HTMLTableRowElementTypeId}; use script::dom::element::{HTMLTableElementTypeId, HTMLTableHeaderCellElementTypeId};
use script::dom::element::{HTMLTableRowElementTypeId, HTMLTableSectionElementTypeId};
use script::dom::node::{CommentNodeTypeId, DoctypeNodeTypeId, DocumentFragmentNodeTypeId}; use script::dom::node::{CommentNodeTypeId, DoctypeNodeTypeId, DocumentFragmentNodeTypeId};
use script::dom::node::{DocumentNodeTypeId, ElementNodeTypeId, ProcessingInstructionNodeTypeId}; use script::dom::node::{DocumentNodeTypeId, ElementNodeTypeId, ProcessingInstructionNodeTypeId};
use script::dom::node::{TextNodeTypeId}; use script::dom::node::{TextNodeTypeId};
use script::dom::text::Text; use script::dom::text::Text;
use style::computed_values::{display, position, float, white_space}; use servo_util::geometry::Au;
use style::ComputedValues;
use servo_util::namespace; use servo_util::namespace;
use servo_util::url::parse_url; use servo_util::smallvec::SmallVec;
use servo_util::url::is_image_data;
use servo_util::str::is_whitespace; use servo_util::str::is_whitespace;
use servo_util::url::{is_image_data, parse_url};
use extra::url::Url;
use sync::Arc;
use std::mem; use std::mem;
use std::num::Zero; use std::num::Zero;
use style::ComputedValues;
use style::computed_values::{display, position, float, white_space};
use sync::Arc;
/// The results of flow construction for a DOM node. /// The results of flow construction for a DOM node.
pub enum ConstructionResult { pub enum ConstructionResult {
@ -75,7 +78,7 @@ pub enum ConstructionResult {
/// This node contributed a flow at the proper position in the tree. /// This node contributed a flow at the proper position in the tree.
/// Nothing more needs to be done for this node. It has bubbled up fixed /// Nothing more needs to be done for this node. It has bubbled up fixed
/// and absolute descendant flows that have a CB above it. /// and absolute descendant flows that have a CB above it.
FlowConstructionResult(~Flow, AbsDescendants, FixedDescendants), FlowConstructionResult(~Flow, AbsDescendants),
/// This node contributed some object or objects that will be needed to construct a proper flow /// This node contributed some object or objects that will be needed to construct a proper flow
/// later up the tree, but these objects have not yet found their home. /// later up the tree, but these objects have not yet found their home.
@ -86,7 +89,7 @@ impl ConstructionResult {
fn destroy(&mut self) { fn destroy(&mut self) {
match *self { match *self {
NoConstructionResult => {} NoConstructionResult => {}
FlowConstructionResult(ref mut flow, _, _) => flow.destroy(), FlowConstructionResult(ref mut flow, _) => flow.destroy(),
ConstructionItemConstructionResult(ref mut item) => item.destroy(), ConstructionItemConstructionResult(ref mut item) => item.destroy(),
} }
} }
@ -132,9 +135,6 @@ struct InlineBoxesConstructionResult {
/// Any absolute descendants that we're bubbling up. /// Any absolute descendants that we're bubbling up.
abs_descendants: AbsDescendants, abs_descendants: AbsDescendants,
/// Any fixed descendants that we're bubbling up.
fixed_descendants: FixedDescendants,
} }
/// Represents an {ib} split that has not yet found the containing block that it belongs to. This /// Represents an {ib} split that has not yet found the containing block that it belongs to. This
@ -281,7 +281,8 @@ impl<'a> FlowConstructor<'a> {
} }
/// Builds the `ImageBoxInfo` for the given image. This is out of line to guide inlining. /// Builds the `ImageBoxInfo` for the given image. This is out of line to guide inlining.
fn build_box_info_for_image(&mut self, node: &ThreadSafeLayoutNode, url: Option<Url>) -> SpecificBoxInfo { fn build_box_info_for_image(&mut self, node: &ThreadSafeLayoutNode, url: Option<Url>)
-> SpecificBoxInfo {
match url { match url {
None => GenericBox, None => GenericBox,
Some(url) => { Some(url) => {
@ -296,19 +297,25 @@ impl<'a> FlowConstructor<'a> {
pub fn build_specific_box_info_for_node(&mut self, node: &ThreadSafeLayoutNode) pub fn build_specific_box_info_for_node(&mut self, node: &ThreadSafeLayoutNode)
-> SpecificBoxInfo { -> SpecificBoxInfo {
match node.type_id() { match node.type_id() {
ElementNodeTypeId(HTMLImageElementTypeId) => self.build_box_info_for_image(node, node.image_url()), Some(ElementNodeTypeId(HTMLImageElementTypeId)) => {
ElementNodeTypeId(HTMLIFrameElementTypeId) => IframeBox(IframeBoxInfo::new(node)), self.build_box_info_for_image(node, node.image_url())
ElementNodeTypeId(HTMLObjectElementTypeId) => { }
Some(ElementNodeTypeId(HTMLIFrameElementTypeId)) => {
IframeBox(IframeBoxInfo::new(node))
}
Some(ElementNodeTypeId(HTMLObjectElementTypeId)) => {
let data = node.get_object_data(&self.layout_context.url); let data = node.get_object_data(&self.layout_context.url);
self.build_box_info_for_image(node, data) self.build_box_info_for_image(node, data)
} }
ElementNodeTypeId(HTMLTableElementTypeId) => TableWrapperBox, Some(ElementNodeTypeId(HTMLTableElementTypeId)) => TableWrapperBox,
ElementNodeTypeId(HTMLTableColElementTypeId) => TableColumnBox(TableColumnBoxInfo::new(node)), Some(ElementNodeTypeId(HTMLTableColElementTypeId)) => {
ElementNodeTypeId(HTMLTableDataCellElementTypeId) | TableColumnBox(TableColumnBoxInfo::new(node))
ElementNodeTypeId(HTMLTableHeaderCellElementTypeId) => TableCellBox, }
ElementNodeTypeId(HTMLTableRowElementTypeId) | Some(ElementNodeTypeId(HTMLTableDataCellElementTypeId)) |
ElementNodeTypeId(HTMLTableSectionElementTypeId) => TableRowBox, Some(ElementNodeTypeId(HTMLTableHeaderCellElementTypeId)) => TableCellBox,
TextNodeTypeId => UnscannedTextBox(UnscannedTextBoxInfo::new(node)), Some(ElementNodeTypeId(HTMLTableRowElementTypeId)) |
Some(ElementNodeTypeId(HTMLTableSectionElementTypeId)) => TableRowBox,
None | Some(TextNodeTypeId) => UnscannedTextBox(UnscannedTextBoxInfo::new(node)),
_ => GenericBox, _ => GenericBox,
} }
} }
@ -352,6 +359,115 @@ impl<'a> FlowConstructor<'a> {
} }
} }
fn build_block_flow_using_children_construction_result(&mut self,
flow: &mut ~Flow,
consecutive_siblings: &mut ~[~Flow],
node: &ThreadSafeLayoutNode,
kid: ThreadSafeLayoutNode,
opt_boxes_for_inline_flow: &mut Option<~[Box]>,
abs_descendants: &mut Descendants,
first_box: &mut bool) {
match kid.swap_out_construction_result() {
NoConstructionResult => {}
FlowConstructionResult(kid_flow, kid_abs_descendants) => {
// If kid_flow is TableCaptionFlow, kid_flow should be added under
// TableWrapperFlow.
if flow.is_table() && kid_flow.is_table_caption() {
kid.set_flow_construction_result(FlowConstructionResult(
kid_flow,
Descendants::new()))
} else if flow.need_anonymous_flow(kid_flow) {
consecutive_siblings.push(kid_flow)
} else {
// Strip ignorable whitespace from the start of this flow per CSS 2.1 §
// 9.2.1.1.
if flow.is_table_kind() || *first_box {
strip_ignorable_whitespace_from_start(opt_boxes_for_inline_flow);
*first_box = false
}
// Flush any inline boxes that we were gathering up. This allows us to handle
// {ib} splits.
debug!("flushing {} inline box(es) to flow A",
opt_boxes_for_inline_flow.as_ref()
.map_or(0, |boxes| boxes.len()));
self.flush_inline_boxes_to_flow_or_list_if_necessary(
opt_boxes_for_inline_flow,
flow,
consecutive_siblings,
node);
if !consecutive_siblings.is_empty() {
let consecutive_siblings = mem::replace(consecutive_siblings, ~[]);
self.generate_anonymous_missing_child(consecutive_siblings,
flow,
node);
}
flow.add_new_child(kid_flow);
}
abs_descendants.push_descendants(kid_abs_descendants);
}
ConstructionItemConstructionResult(InlineBoxesConstructionItem(
InlineBoxesConstructionResult {
splits: opt_splits,
boxes: boxes,
abs_descendants: kid_abs_descendants,
})) => {
// Add any {ib} splits.
match opt_splits {
None => {}
Some(splits) => {
for split in splits.move_iter() {
// Pull apart the {ib} split object and push its predecessor boxes
// onto the list.
let InlineBlockSplit {
predecessor_boxes: predecessor_boxes,
flow: kid_flow
} = split;
opt_boxes_for_inline_flow.push_all_move(predecessor_boxes);
// If this is the first box in flow, then strip ignorable
// whitespace per CSS 2.1 § 9.2.1.1.
if *first_box {
strip_ignorable_whitespace_from_start(
opt_boxes_for_inline_flow);
*first_box = false
}
// Flush any inline boxes that we were gathering up.
debug!("flushing {} inline box(es) to flow A",
opt_boxes_for_inline_flow.as_ref()
.map_or(0, |boxes| boxes.len()));
self.flush_inline_boxes_to_flow_or_list_if_necessary(
opt_boxes_for_inline_flow,
flow,
consecutive_siblings,
node);
// Push the flow generated by the {ib} split onto our list of
// flows.
if flow.need_anonymous_flow(kid_flow) {
consecutive_siblings.push(kid_flow)
} else {
flow.add_new_child(kid_flow)
}
}
}
}
// Add the boxes to the list we're maintaining.
opt_boxes_for_inline_flow.push_all_move(boxes);
abs_descendants.push_descendants(kid_abs_descendants);
}
ConstructionItemConstructionResult(WhitespaceConstructionItem(..)) => {
// Nothing to do here.
}
ConstructionItemConstructionResult(TableColumnBoxConstructionItem(_)) => {
// TODO: Implement anonymous table objects for missing parents
// CSS 2.1 § 17.2.1, step 3-2
}
}
}
/// Build block flow for current node using information from children nodes. /// Build block flow for current node using information from children nodes.
/// ///
/// Consume results from children and combine them, handling {ib} splits. /// Consume results from children and combine them, handling {ib} splits.
@ -367,109 +483,22 @@ impl<'a> FlowConstructor<'a> {
let mut opt_boxes_for_inline_flow = None; let mut opt_boxes_for_inline_flow = None;
let mut consecutive_siblings = ~[]; let mut consecutive_siblings = ~[];
let mut first_box = true; let mut first_box = true;
// List of absolute descendants, in tree order. // List of absolute descendants, in tree order.
let mut abs_descendants = Descendants::new(); let mut abs_descendants = Descendants::new();
let mut fixed_descendants = Descendants::new();
for kid in node.children() { for kid in node.children() {
match kid.swap_out_construction_result() { if kid.get_element_type() != Normal {
NoConstructionResult => {} self.process(&kid);
FlowConstructionResult(kid_flow, kid_abs_descendants, kid_fixed_descendants) => {
// If kid_flow is TableCaptionFlow, kid_flow should be added under TableWrapperFlow.
if flow.is_table() && kid_flow.is_table_caption() {
kid.set_flow_construction_result(FlowConstructionResult(kid_flow,
Descendants::new(),
Descendants::new()))
} else if flow.need_anonymous_flow(kid_flow) {
consecutive_siblings.push(kid_flow)
} else {
// Strip ignorable whitespace from the start of this flow per CSS 2.1 §
// 9.2.1.1.
if flow.is_table_kind() || first_box {
strip_ignorable_whitespace_from_start(&mut opt_boxes_for_inline_flow);
first_box = false
}
// Flush any inline boxes that we were gathering up. This allows us to handle
// {ib} splits.
debug!("flushing {} inline box(es) to flow A",
opt_boxes_for_inline_flow.as_ref()
.map_or(0, |boxes| boxes.len()));
self.flush_inline_boxes_to_flow_or_list_if_necessary(&mut opt_boxes_for_inline_flow,
&mut flow,
&mut consecutive_siblings,
node);
if !consecutive_siblings.is_empty() {
self.generate_anonymous_missing_child(consecutive_siblings, &mut flow, node);
consecutive_siblings = ~[];
}
flow.add_new_child(kid_flow);
}
abs_descendants.push_descendants(kid_abs_descendants);
fixed_descendants.push_descendants(kid_fixed_descendants);
}
ConstructionItemConstructionResult(InlineBoxesConstructionItem(
InlineBoxesConstructionResult {
splits: opt_splits,
boxes: boxes,
abs_descendants: kid_abs_descendants,
fixed_descendants: kid_fixed_descendants,
})) => {
// Add any {ib} splits.
match opt_splits {
None => {}
Some(splits) => {
for split in splits.move_iter() {
// Pull apart the {ib} split object and push its predecessor boxes
// onto the list.
let InlineBlockSplit {
predecessor_boxes: predecessor_boxes,
flow: kid_flow
} = split;
opt_boxes_for_inline_flow.push_all_move(predecessor_boxes);
// If this is the first box in flow, then strip ignorable
// whitespace per CSS 2.1 § 9.2.1.1.
if first_box {
strip_ignorable_whitespace_from_start(
&mut opt_boxes_for_inline_flow);
first_box = false
}
// Flush any inline boxes that we were gathering up.
debug!("flushing {} inline box(es) to flow A",
opt_boxes_for_inline_flow.as_ref()
.map_or(0,
|boxes| boxes.len()));
self.flush_inline_boxes_to_flow_or_list_if_necessary(
&mut opt_boxes_for_inline_flow,
&mut flow,
&mut consecutive_siblings,
node);
// Push the flow generated by the {ib} split onto our list of
// flows.
if flow.need_anonymous_flow(kid_flow) {
consecutive_siblings.push(kid_flow)
} else {
flow.add_new_child(kid_flow)
}
}
}
}
// Add the boxes to the list we're maintaining.
opt_boxes_for_inline_flow.push_all_move(boxes);
abs_descendants.push_descendants(kid_abs_descendants);
fixed_descendants.push_descendants(kid_fixed_descendants);
}
ConstructionItemConstructionResult(WhitespaceConstructionItem(..)) => {
// Nothing to do here.
}
ConstructionItemConstructionResult(TableColumnBoxConstructionItem(_)) => {
// TODO: Implement anonymous table objects for missing parents
// CSS 2.1 § 17.2.1, step 3-2
}
} }
self.build_block_flow_using_children_construction_result(
&mut flow,
&mut consecutive_siblings,
node,
kid,
&mut opt_boxes_for_inline_flow,
&mut abs_descendants,
&mut first_box);
} }
// Perform a final flush of any inline boxes that we were gathering up to handle {ib} // Perform a final flush of any inline boxes that we were gathering up to handle {ib}
@ -493,16 +522,13 @@ impl<'a> FlowConstructor<'a> {
flow.set_abs_descendants(abs_descendants); flow.set_abs_descendants(abs_descendants);
abs_descendants = Descendants::new(); abs_descendants = Descendants::new();
if is_fixed_positioned { if is_fixed_positioned || is_absolutely_positioned {
// Send itself along with the other fixed descendants.
fixed_descendants.push(Rawlink::some(flow));
} else if is_absolutely_positioned {
// This is now the only absolute flow in the subtree which hasn't yet // This is now the only absolute flow in the subtree which hasn't yet
// reached its CB. // reached its CB.
abs_descendants.push(Rawlink::some(flow)); abs_descendants.push(Rawlink::some(flow));
} }
} }
FlowConstructionResult(flow, abs_descendants, fixed_descendants) FlowConstructionResult(flow, abs_descendants)
} }
/// Builds a flow for a node with `display: block`. This yields a `BlockFlow` with possibly /// Builds a flow for a node with `display: block`. This yields a `BlockFlow` with possibly
@ -521,7 +547,6 @@ impl<'a> FlowConstructor<'a> {
self.build_flow_using_children(flow, node) self.build_flow_using_children(flow, node)
} }
/// Concatenates the boxes of kids, adding in our own borders/padding/margins if necessary. /// Concatenates the boxes of kids, adding in our own borders/padding/margins if necessary.
/// Returns the `InlineBoxesConstructionResult`, if any. There will be no /// Returns the `InlineBoxesConstructionResult`, if any. There will be no
/// `InlineBoxesConstructionResult` if this node consisted entirely of ignorable whitespace. /// `InlineBoxesConstructionResult` if this node consisted entirely of ignorable whitespace.
@ -530,13 +555,12 @@ impl<'a> FlowConstructor<'a> {
let mut opt_inline_block_splits = None; let mut opt_inline_block_splits = None;
let mut opt_box_accumulator = None; let mut opt_box_accumulator = None;
let mut abs_descendants = Descendants::new(); let mut abs_descendants = Descendants::new();
let mut fixed_descendants = Descendants::new();
// Concatenate all the boxes of our kids, creating {ib} splits as necessary. // Concatenate all the boxes of our kids, creating {ib} splits as necessary.
for kid in node.children() { for kid in node.children() {
match kid.swap_out_construction_result() { match kid.swap_out_construction_result() {
NoConstructionResult => {} NoConstructionResult => {}
FlowConstructionResult(flow, kid_abs_descendants, kid_fixed_descendants) => { FlowConstructionResult(flow, kid_abs_descendants) => {
// {ib} split. Flush the accumulator to our new split and make a new // {ib} split. Flush the accumulator to our new split and make a new
// accumulator to hold any subsequent boxes we come across. // accumulator to hold any subsequent boxes we come across.
let split = InlineBlockSplit { let split = InlineBlockSplit {
@ -545,14 +569,12 @@ impl<'a> FlowConstructor<'a> {
}; };
opt_inline_block_splits.push(split); opt_inline_block_splits.push(split);
abs_descendants.push_descendants(kid_abs_descendants); abs_descendants.push_descendants(kid_abs_descendants);
fixed_descendants.push_descendants(kid_fixed_descendants);
} }
ConstructionItemConstructionResult(InlineBoxesConstructionItem( ConstructionItemConstructionResult(InlineBoxesConstructionItem(
InlineBoxesConstructionResult { InlineBoxesConstructionResult {
splits: opt_splits, splits: opt_splits,
boxes: boxes, boxes: boxes,
abs_descendants: kid_abs_descendants, abs_descendants: kid_abs_descendants,
fixed_descendants: kid_fixed_descendants,
})) => { })) => {
// Bubble up {ib} splits. // Bubble up {ib} splits.
@ -579,7 +601,6 @@ impl<'a> FlowConstructor<'a> {
// Push residual boxes. // Push residual boxes.
opt_box_accumulator.push_all_move(boxes); opt_box_accumulator.push_all_move(boxes);
abs_descendants.push_descendants(kid_abs_descendants); abs_descendants.push_descendants(kid_abs_descendants);
fixed_descendants.push_descendants(kid_fixed_descendants);
} }
ConstructionItemConstructionResult(WhitespaceConstructionItem(whitespace_node, ConstructionItemConstructionResult(WhitespaceConstructionItem(whitespace_node,
whitespace_style)) whitespace_style))
@ -612,7 +633,7 @@ impl<'a> FlowConstructor<'a> {
for box_ in boxes.iter() { for box_ in boxes.iter() {
total.push(box_); total.push(box_);
} }
self.set_inline_info_for_inline_child(&total, node); self.set_inline_info_for_inline_child(total, node);
}, },
None => { None => {
@ -622,7 +643,7 @@ impl<'a> FlowConstructor<'a> {
total.push(box_); total.push(box_);
} }
} }
self.set_inline_info_for_inline_child(&total, node); self.set_inline_info_for_inline_child(total, node);
} }
} }
}, },
@ -633,7 +654,7 @@ impl<'a> FlowConstructor<'a> {
for box_ in boxes.iter() { for box_ in boxes.iter() {
total.push(box_); total.push(box_);
} }
self.set_inline_info_for_inline_child(&total, node); self.set_inline_info_for_inline_child(total, node);
}, },
None => {} None => {}
} }
@ -648,7 +669,6 @@ impl<'a> FlowConstructor<'a> {
splits: opt_inline_block_splits, splits: opt_inline_block_splits,
boxes: opt_box_accumulator.to_vec(), boxes: opt_box_accumulator.to_vec(),
abs_descendants: abs_descendants, abs_descendants: abs_descendants,
fixed_descendants: fixed_descendants,
}); });
ConstructionItemConstructionResult(construction_item) ConstructionItemConstructionResult(construction_item)
} else { } else {
@ -656,8 +676,9 @@ impl<'a> FlowConstructor<'a> {
} }
} }
// FIXME(#1999, pcwalton): Why does this function create a box only to throw it away???
fn set_inline_info_for_inline_child(&mut self, fn set_inline_info_for_inline_child(&mut self,
boxes: &~[&Box], boxes: &[&Box],
parent_node: &ThreadSafeLayoutNode) { parent_node: &ThreadSafeLayoutNode) {
let parent_box = Box::new(self, parent_node); let parent_box = Box::new(self, parent_node);
let font_style = parent_box.font_style(); let font_style = parent_box.font_style();
@ -671,33 +692,39 @@ impl<'a> FlowConstructor<'a> {
let boxes_len = boxes.len(); let boxes_len = boxes.len();
parent_box.compute_borders(parent_box.style()); parent_box.compute_borders(parent_box.style());
// FIXME(#2000, pcwalton): I suspect that `Au(0)` is not correct for the containing block
// width.
parent_box.compute_padding(parent_box.style(), Au(0));
for (i, box_) in boxes.iter().enumerate() { for (i, box_) in boxes.iter().enumerate() {
if box_.inline_info.with( |data| data.is_none() ) { if box_.inline_info.with( |data| data.is_none() ) {
box_.inline_info.set(Some(InlineInfo::new())); box_.inline_info.set(Some(InlineInfo::new()));
} }
let mut border = parent_box.border.get(); let mut border = parent_box.border.get();
let mut padding = parent_box.padding.get();
if i != 0 { if i != 0 {
border.left = Zero::zero(); border.left = Zero::zero();
padding.left = Zero::zero()
} }
if i != (boxes_len - 1) { if i != (boxes_len - 1) {
border.right = Zero::zero(); border.right = Zero::zero();
padding.right = Zero::zero()
} }
let mut info = box_.inline_info.borrow_mut(); let mut info = box_.inline_info.borrow_mut();
match info.get() { match info.get() {
&Some(ref mut info) => { &Some(ref mut info) => {
// TODO(ksh8281) compute margin,padding // TODO(ksh8281): Compute margins.
info.parent_info.push( info.parent_info.push(InlineParentInfo {
InlineParentInfo { padding: padding,
padding: Zero::zero(), border: border,
border: border, margin: Zero::zero(),
margin: Zero::zero(), style: parent_box.style.clone(),
style: parent_box.style.clone(), font_ascent: font_ascent,
font_ascent: font_ascent, font_descent: font_descent,
font_descent: font_descent, node: OpaqueNodeMethods::from_thread_safe_layout_node(parent_node),
node: OpaqueNode::from_thread_safe_layout_node(parent_node), })
});
}, },
&None => {} &None => {}
} }
@ -712,20 +739,22 @@ impl<'a> FlowConstructor<'a> {
} }
// If this node is ignorable whitespace, bail out now. // If this node is ignorable whitespace, bail out now.
//
// FIXME(#2001, pcwalton): Don't do this if there's padding or borders.
if node.is_ignorable_whitespace() { if node.is_ignorable_whitespace() {
let opaque_node = OpaqueNode::from_thread_safe_layout_node(node); let opaque_node = OpaqueNodeMethods::from_thread_safe_layout_node(node);
return ConstructionItemConstructionResult(WhitespaceConstructionItem( return ConstructionItemConstructionResult(WhitespaceConstructionItem(
opaque_node, opaque_node,
node.style().clone())) node.style().clone()))
} }
let mut opt_box_accumulator = None;
opt_box_accumulator.push(Box::new(self, node));
let construction_item = InlineBoxesConstructionItem(InlineBoxesConstructionResult { let construction_item = InlineBoxesConstructionItem(InlineBoxesConstructionResult {
splits: None, splits: None,
boxes: ~[ boxes: opt_box_accumulator.to_vec(),
Box::new(self, node)
],
abs_descendants: Descendants::new(), abs_descendants: Descendants::new(),
fixed_descendants: Descendants::new(),
}); });
ConstructionItemConstructionResult(construction_item) ConstructionItemConstructionResult(construction_item)
} }
@ -750,7 +779,7 @@ impl<'a> FlowConstructor<'a> {
for kid in node.children() { for kid in node.children() {
match kid.swap_out_construction_result() { match kid.swap_out_construction_result() {
NoConstructionResult | ConstructionItemConstructionResult(_) => {} NoConstructionResult | ConstructionItemConstructionResult(_) => {}
FlowConstructionResult(kid_flow, _, _) => { FlowConstructionResult(kid_flow, _) => {
// Only kid flows with table-caption are matched here. // Only kid flows with table-caption are matched here.
assert!(kid_flow.is_table_caption()); assert!(kid_flow.is_table_caption());
table_wrapper_flow.add_new_child(kid_flow); table_wrapper_flow.add_new_child(kid_flow);
@ -761,8 +790,10 @@ impl<'a> FlowConstructor<'a> {
/// Generates an anonymous table flow according to CSS 2.1 § 17.2.1, step 2. /// Generates an anonymous table flow according to CSS 2.1 § 17.2.1, step 2.
/// If necessary, generate recursively another anonymous table flow. /// If necessary, generate recursively another anonymous table flow.
fn generate_anonymous_missing_child(&mut self, child_flows: ~[~Flow], fn generate_anonymous_missing_child(&mut self,
flow: &mut ~Flow, node: &ThreadSafeLayoutNode) { child_flows: ~[~Flow],
flow: &mut ~Flow,
node: &ThreadSafeLayoutNode) {
let mut anonymous_flow = flow.generate_missing_child_flow(node); let mut anonymous_flow = flow.generate_missing_child_flow(node);
let mut consecutive_siblings = ~[]; let mut consecutive_siblings = ~[];
for kid_flow in child_flows.move_iter() { for kid_flow in child_flows.move_iter() {
@ -805,10 +836,9 @@ impl<'a> FlowConstructor<'a> {
// NOTE: The order of captions and table are not the same order as in the DOM tree. // NOTE: The order of captions and table are not the same order as in the DOM tree.
// All caption blocks are placed before the table flow // All caption blocks are placed before the table flow
match construction_result { match construction_result {
FlowConstructionResult(table_flow, table_abs_descendants, table_fixed_descendants) => { FlowConstructionResult(table_flow, table_abs_descendants) => {
wrapper_flow.add_new_child(table_flow); wrapper_flow.add_new_child(table_flow);
abs_descendants.push_descendants(table_abs_descendants); abs_descendants.push_descendants(table_abs_descendants);
fixed_descendants.push_descendants(table_fixed_descendants);
} }
_ => {} _ => {}
} }
@ -832,7 +862,7 @@ impl<'a> FlowConstructor<'a> {
abs_descendants.push(Rawlink::some(wrapper_flow)); abs_descendants.push(Rawlink::some(wrapper_flow));
} }
} }
FlowConstructionResult(wrapper_flow, abs_descendants, fixed_descendants) FlowConstructionResult(wrapper_flow, abs_descendants)
} }
/// Builds a flow for a node with `display: table-caption`. This yields a `TableCaptionFlow` /// Builds a flow for a node with `display: table-caption`. This yields a `TableCaptionFlow`
@ -904,7 +934,7 @@ impl<'a> FlowConstructor<'a> {
let mut flow = ~TableColGroupFlow::from_node_and_boxes(node, box_, col_boxes) as ~Flow; let mut flow = ~TableColGroupFlow::from_node_and_boxes(node, box_, col_boxes) as ~Flow;
flow.finish(self.layout_context); flow.finish(self.layout_context);
FlowConstructionResult(flow, Descendants::new(), Descendants::new()) FlowConstructionResult(flow, Descendants::new())
} }
} }
@ -922,16 +952,23 @@ impl<'a> PostorderNodeMutTraversal for FlowConstructor<'a> {
fn process(&mut self, node: &ThreadSafeLayoutNode) -> bool { fn process(&mut self, node: &ThreadSafeLayoutNode) -> bool {
// Get the `display` property for this node, and determine whether this node is floated. // Get the `display` property for this node, and determine whether this node is floated.
let (display, float, positioning) = match node.type_id() { let (display, float, positioning) = match node.type_id() {
ElementNodeTypeId(_) => { None => {
// Pseudo-element.
let style = node.style().get();
(display::inline, style.Box.get().float, style.Box.get().position)
}
Some(ElementNodeTypeId(_)) => {
let style = node.style().get(); let style = node.style().get();
(style.Box.get().display, style.Box.get().float, style.Box.get().position) (style.Box.get().display, style.Box.get().float, style.Box.get().position)
} }
TextNodeTypeId => (display::inline, float::none, position::static_), Some(TextNodeTypeId) => (display::inline, float::none, position::static_),
CommentNodeTypeId | Some(CommentNodeTypeId) |
DoctypeNodeTypeId | Some(DoctypeNodeTypeId) |
DocumentFragmentNodeTypeId | Some(DocumentFragmentNodeTypeId) |
DocumentNodeTypeId | Some(DocumentNodeTypeId) |
ProcessingInstructionNodeTypeId => (display::none, float::none, position::static_), Some(ProcessingInstructionNodeTypeId) => {
(display::none, float::none, position::static_)
}
}; };
debug!("building flow for node: {:?} {:?}", display, float); debug!("building flow for node: {:?} {:?}", display, float);
@ -1044,21 +1081,22 @@ trait NodeUtils {
impl<'ln> NodeUtils for ThreadSafeLayoutNode<'ln> { impl<'ln> NodeUtils for ThreadSafeLayoutNode<'ln> {
fn is_replaced_content(&self) -> bool { fn is_replaced_content(&self) -> bool {
match self.type_id() { match self.type_id() {
TextNodeTypeId | Some(TextNodeTypeId) |
ProcessingInstructionNodeTypeId | Some(ProcessingInstructionNodeTypeId) |
CommentNodeTypeId | Some(CommentNodeTypeId) |
DoctypeNodeTypeId | Some(DoctypeNodeTypeId) |
DocumentFragmentNodeTypeId | Some(DocumentFragmentNodeTypeId) |
DocumentNodeTypeId | Some(DocumentNodeTypeId) |
ElementNodeTypeId(HTMLImageElementTypeId) => true, None |
ElementNodeTypeId(HTMLObjectElementTypeId) => self.has_object_data(), Some(ElementNodeTypeId(HTMLImageElementTypeId)) => true,
ElementNodeTypeId(_) => false, Some(ElementNodeTypeId(HTMLObjectElementTypeId)) => self.has_object_data(),
Some(ElementNodeTypeId(_)) => false,
} }
} }
fn is_ignorable_whitespace(&self) -> bool { fn is_ignorable_whitespace(&self) -> bool {
match self.type_id() { match self.type_id() {
TextNodeTypeId => { Some(TextNodeTypeId) => {
unsafe { unsafe {
let text: JS<Text> = TextCast::to(self.get_jsmanaged()).unwrap(); let text: JS<Text> = TextCast::to(self.get_jsmanaged()).unwrap();
if !is_whitespace(text.get().characterdata.data) { if !is_whitespace(text.get().characterdata.data) {
@ -1085,7 +1123,17 @@ impl<'ln> NodeUtils for ThreadSafeLayoutNode<'ln> {
fn set_flow_construction_result(&self, result: ConstructionResult) { fn set_flow_construction_result(&self, result: ConstructionResult) {
let mut layout_data_ref = self.mutate_layout_data(); let mut layout_data_ref = self.mutate_layout_data();
match *layout_data_ref.get() { match *layout_data_ref.get() {
Some(ref mut layout_data) => layout_data.data.flow_construction_result = result, Some(ref mut layout_data) =>{
match self.get_element_type() {
Before | BeforeBlock => {
layout_data.data.before_flow_construction_result = result
},
After | AfterBlock => {
layout_data.data.after_flow_construction_result = result
},
Normal => layout_data.data.flow_construction_result = result,
}
},
None => fail!("no layout data"), None => fail!("no layout data"),
} }
} }
@ -1095,7 +1143,20 @@ impl<'ln> NodeUtils for ThreadSafeLayoutNode<'ln> {
let mut layout_data_ref = self.mutate_layout_data(); let mut layout_data_ref = self.mutate_layout_data();
match *layout_data_ref.get() { match *layout_data_ref.get() {
Some(ref mut layout_data) => { Some(ref mut layout_data) => {
mem::replace(&mut layout_data.data.flow_construction_result, NoConstructionResult) match self.get_element_type() {
Before | BeforeBlock => {
mem::replace(&mut layout_data.data.before_flow_construction_result,
NoConstructionResult)
}
After | AfterBlock => {
mem::replace(&mut layout_data.data.after_flow_construction_result,
NoConstructionResult)
}
Normal => {
mem::replace(&mut layout_data.data.flow_construction_result,
NoConstructionResult)
}
}
} }
None => fail!("no layout data"), None => fail!("no layout data"),
} }

View file

@ -5,10 +5,10 @@
//! Data needed by the layout task. //! Data needed by the layout task.
use css::matching::{ApplicableDeclarationsCache, StyleSharingCandidateCache}; use css::matching::{ApplicableDeclarationsCache, StyleSharingCandidateCache};
use layout::util::OpaqueNode;
use extra::url::Url; use extra::url::Url;
use geom::size::Size2D; use geom::size::Size2D;
use gfx::display_list::OpaqueNode;
use gfx::font_context::{FontContext, FontContextInfo}; use gfx::font_context::{FontContext, FontContextInfo};
use green::task::GreenTask; use green::task::GreenTask;
use script::layout_interface::LayoutChan; use script::layout_interface::LayoutChan;

View file

@ -4,39 +4,34 @@
//! Constructs display lists from boxes. //! Constructs display lists from boxes.
use layout::box_::Box;
use layout::context::LayoutContext; use layout::context::LayoutContext;
use layout::util::OpaqueNode;
use geom::{Point2D, Rect, Size2D};
use gfx::render_task::RenderLayer;
use gfx; use gfx;
use servo_util::geometry::Au;
use servo_util::smallvec::SmallVec0;
use style; use style;
pub trait ExtraDisplayListData { /// Manages the information needed to construct the display list.
fn new(box_: &Box) -> Self;
}
pub type Nothing = ();
impl ExtraDisplayListData for OpaqueNode {
fn new(box_: &Box) -> OpaqueNode {
box_.node
}
}
impl ExtraDisplayListData for Nothing {
fn new(_: &Box) -> Nothing {
()
}
}
/// A builder object that manages display list builder should mainly hold information about the
/// initial request and desired result--for example, whether the `DisplayList` is to be used for
/// painting or hit testing. This can affect which boxes are created.
///
/// Right now, the builder isn't used for much, but it establishes the pattern we'll need once we
/// support display-list-based hit testing and so forth.
pub struct DisplayListBuilder<'a> { pub struct DisplayListBuilder<'a> {
ctx: &'a LayoutContext, ctx: &'a LayoutContext,
/// A list of render layers that we've built up, root layer not included.
layers: SmallVec0<RenderLayer>,
/// The dirty rect.
dirty: Rect<Au>,
}
/// Information needed at each step of the display list building traversal.
pub struct DisplayListBuildingInfo {
/// The size of the containing block for relatively-positioned descendants.
relative_containing_block_size: Size2D<Au>,
/// The position and size of the absolute containing block.
absolute_containing_block_position: Point2D<Au>,
/// Whether the absolute containing block forces positioned descendants to be layerized.
layers_needed_for_positioned_flows: bool,
} }
// //

View file

@ -26,14 +26,16 @@
/// similar methods. /// similar methods.
use css::node_style::StyledNode; use css::node_style::StyledNode;
use layout::block::{BlockFlow}; use layout::block::BlockFlow;
use layout::box_::{Box, TableRowBox, TableCellBox}; use layout::box_::{Box, TableRowBox, TableCellBox};
use layout::context::LayoutContext;
use layout::construct::OptVector; use layout::construct::OptVector;
use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData}; use layout::context::LayoutContext;
use layout::display_list_builder::{DisplayListBuilder, DisplayListBuildingInfo, ToGfxColor};
use layout::floats::Floats; use layout::floats::Floats;
use layout::flow_list::{FlowList, Link, Rawlink, FlowListIterator, MutFlowListIterator};
use layout::incremental::RestyleDamage; use layout::incremental::RestyleDamage;
use layout::inline::InlineFlow; use layout::inline::InlineFlow;
use layout::model::{CollapsibleMargins, IntrinsicWidths, MarginCollapseInfo};
use layout::parallel::FlowParallelInfo; use layout::parallel::FlowParallelInfo;
use layout::parallel; use layout::parallel;
use layout::table_wrapper::TableWrapperFlow; use layout::table_wrapper::TableWrapperFlow;
@ -44,24 +46,22 @@ use layout::table_row::TableRowFlow;
use layout::table_caption::TableCaptionFlow; use layout::table_caption::TableCaptionFlow;
use layout::table_cell::TableCellFlow; use layout::table_cell::TableCellFlow;
use layout::wrapper::ThreadSafeLayoutNode; use layout::wrapper::ThreadSafeLayoutNode;
use layout::flow_list::{FlowList, Link, Rawlink, FlowListIterator, MutFlowListIterator};
use collections::Deque; use collections::Deque;
use geom::point::Point2D;
use geom::Size2D; use geom::Size2D;
use geom::point::Point2D;
use geom::rect::Rect; use geom::rect::Rect;
use gfx::display_list::{ClipDisplayItemClass, DisplayListCollection, DisplayList};
use layout::display_list_builder::ToGfxColor;
use gfx::color::Color; use gfx::color::Color;
use servo_util::smallvec::{SmallVec, SmallVec0}; use gfx::display_list::StackingContext;
use servo_msg::compositor_msg::LayerId;
use servo_util::geometry::Au; use servo_util::geometry::Au;
use servo_util::smallvec::{SmallVec, SmallVec0};
use std::cast; use std::cast;
use std::cell::RefCell; use std::iter::Zip;
use std::sync::atomics::Relaxed; use std::sync::atomics::Relaxed;
use std::vec::MutItems; use std::vec::MutItems;
use std::iter::Zip;
use style::ComputedValues; use style::ComputedValues;
use style::computed_values::{text_align, position}; use style::computed_values::{clear, position, text_align};
/// Virtual methods that make up a float context. /// Virtual methods that make up a float context.
/// ///
@ -126,6 +126,24 @@ pub trait Flow {
fail!("called as_table_cell() on a non-tablecell flow") fail!("called as_table_cell() on a non-tablecell flow")
} }
/// If this is a table row or table rowgroup or table flow, returns column widths.
/// Fails otherwise.
fn col_widths<'a>(&'a mut self) -> &'a mut ~[Au] {
fail!("called col_widths() on an other flow than table-row/table-rowgroup/table")
}
/// If this is a table row flow or table rowgroup flow or table flow, returns column min widths.
/// Fails otherwise.
fn col_min_widths<'a>(&'a self) -> &'a ~[Au] {
fail!("called col_min_widths() on an other flow than table-row/table-rowgroup/table")
}
/// If this is a table row flow or table rowgroup flow or table flow, returns column min widths.
/// Fails otherwise.
fn col_pref_widths<'a>(&'a self) -> &'a ~[Au] {
fail!("called col_pref_widths() on an other flow than table-row/table-rowgroup/table")
}
// Main methods // Main methods
/// Pass 1 of reflow: computes minimum and preferred widths. /// Pass 1 of reflow: computes minimum and preferred widths.
@ -148,15 +166,10 @@ pub trait Flow {
fail!("assign_height_inorder not yet implemented") fail!("assign_height_inorder not yet implemented")
} }
/// Collapses margins with the parent flow. This runs as part of assign-heights. fn compute_collapsible_top_margin(&mut self,
fn collapse_margins(&mut self, _layout_context: &mut LayoutContext,
_top_margin_collapsible: bool, _margin_collapse_info: &mut MarginCollapseInfo) {
_first_in_flow: &mut bool, // The default implementation is a no-op.
_margin_top: &mut Au,
_top_offset: &mut Au,
_collapsing: &mut Au,
_collapsible: &mut Au) {
fail!("collapse_margins not yet implemented")
} }
/// Marks this flow as the root flow. The default implementation is a no-op. /// Marks this flow as the root flow. The default implementation is a no-op.
@ -209,6 +222,11 @@ pub trait Flow {
false false
} }
/// Returns true if this is an absolute containing block.
fn is_absolute_containing_block(&self) -> bool {
false
}
/// Return the dimensions of the CB generated _by_ this flow for absolute descendants. /// Return the dimensions of the CB generated _by_ this flow for absolute descendants.
fn generated_cb_size(&self) -> Size2D<Au> { fn generated_cb_size(&self) -> Size2D<Au> {
fail!("generated_cb_size not yet implemented") fail!("generated_cb_size not yet implemented")
@ -219,6 +237,14 @@ pub trait Flow {
fail!("this is not the CB-generating flow you're looking for") fail!("this is not the CB-generating flow you're looking for")
} }
/// Returns a layer ID for the given fragment.
fn layer_id(&self, fragment_id: uint) -> LayerId {
unsafe {
let pointer: uint = cast::transmute(self);
LayerId(pointer, fragment_id)
}
}
/// Returns a debugging string describing this flow. /// Returns a debugging string describing this flow.
fn debug_str(&self) -> ~str { fn debug_str(&self) -> ~str {
~"???" ~"???"
@ -336,16 +362,11 @@ pub trait MutableFlowUtils {
/// Computes the overflow region for this flow. /// Computes the overflow region for this flow.
fn store_overflow(self, _: &mut LayoutContext); fn store_overflow(self, _: &mut LayoutContext);
/// builds the display lists /// Builds the display lists for this flow and its descendants.
fn build_display_lists<E:ExtraDisplayListData>( fn build_display_list(self,
self, stacking_context: &mut StackingContext,
builder: &DisplayListBuilder, builder: &mut DisplayListBuilder,
container_block_size: &Size2D<Au>, info: &DisplayListBuildingInfo);
absolute_cb_abs_position: Point2D<Au>,
dirty: &Rect<Au>,
index: uint,
mut list: &RefCell<DisplayListCollection<E>>)
-> bool;
/// Destroys the flow. /// Destroys the flow.
fn destroy(self); fn destroy(self);
@ -369,11 +390,6 @@ pub trait MutableOwnedFlowUtils {
/// Set this flow as the Containing Block for all the absolute descendants. /// Set this flow as the Containing Block for all the absolute descendants.
fn set_abs_descendants(&mut self, abs_descendants: AbsDescendants); fn set_abs_descendants(&mut self, abs_descendants: AbsDescendants);
/// Set fixed descendants for this flow.
///
/// Set yourself as the Containing Block for all the fixed descendants.
fn set_fixed_descendants(&mut self, fixed_descendants: AbsDescendants);
/// Destroys the flow. /// Destroys the flow.
fn destroy(&mut self); fn destroy(&mut self);
} }
@ -606,6 +622,17 @@ bitfield!(FlowFlags, override_overline, set_override_overline, 0b0000_0100)
// NB: If you update this, you need to update TEXT_DECORATION_OVERRIDE_BITMASK. // NB: If you update this, you need to update TEXT_DECORATION_OVERRIDE_BITMASK.
bitfield!(FlowFlags, override_line_through, set_override_line_through, 0b0000_1000) bitfield!(FlowFlags, override_line_through, set_override_line_through, 0b0000_1000)
// Whether this flow contains a flow that has its own layer within the same absolute containing
// block.
bitfield!(FlowFlags,
layers_needed_for_descendants,
set_layers_needed_for_descendants,
0b0100_0000)
// Whether this flow must have its own layer. Even if this flag is not set, it might get its own
// layer if it's deemed to be likely to overlap flows with their own layer.
bitfield!(FlowFlags, needs_layer, set_needs_layer, 0b1000_0000)
// The text alignment for this flow. // The text alignment for this flow.
impl FlowFlags { impl FlowFlags {
#[inline] #[inline]
@ -690,7 +717,6 @@ impl Descendants {
} }
pub type AbsDescendants = Descendants; pub type AbsDescendants = Descendants;
pub type FixedDescendants = Descendants;
type DescendantIter<'a> = MutItems<'a, Rawlink>; type DescendantIter<'a> = MutItems<'a, Rawlink>;
@ -708,12 +734,16 @@ pub struct BaseFlow {
/* layout computations */ /* layout computations */
// TODO: min/pref and position are used during disjoint phases of // TODO: min/pref and position are used during disjoint phases of
// layout; maybe combine into a single enum to save space. // layout; maybe combine into a single enum to save space.
min_width: Au, intrinsic_widths: IntrinsicWidths,
pref_width: Au,
/// The upper left corner of the box representing this flow, relative to /// The upper left corner of the box representing this flow, relative to the box representing
/// the box representing its parent flow. /// its parent flow.
/// For absolute flows, this represents the position wrt to its Containing Block. ///
/// For absolute flows, this represents the position with respect to its *containing block*.
///
/// This does not include margins in the block flow direction, because those can collapse. So
/// for the block direction (usually vertical), this represents the *border box*. For the
/// inline direction (usually horizontal), this represents the *margin box*.
position: Rect<Au>, position: Rect<Au>,
/// The amount of overflow of this flow, relative to the containing block. Must include all the /// The amount of overflow of this flow, relative to the containing block. Must include all the
@ -728,6 +758,9 @@ pub struct BaseFlow {
/// The floats next to this flow. /// The floats next to this flow.
floats: Floats, floats: Floats,
/// The value of this flow's `clear` property, if any.
clear: clear::T,
/// For normal flows, this is the number of floated descendants that are /// For normal flows, this is the number of floated descendants that are
/// not contained within any other floated descendant of this flow. For /// not contained within any other floated descendant of this flow. For
/// floats, it is 1. /// floats, it is 1.
@ -735,15 +768,15 @@ pub struct BaseFlow {
/// decide whether to do an in-order traversal for assign_height. /// decide whether to do an in-order traversal for assign_height.
num_floats: uint, num_floats: uint,
/// The collapsible margins for this flow, if any.
collapsible_margins: CollapsibleMargins,
/// The position of this flow in page coordinates, computed during display list construction. /// The position of this flow in page coordinates, computed during display list construction.
abs_position: Point2D<Au>, abs_position: Point2D<Au>,
/// Details about descendants with position 'absolute' for which we are /// Details about descendants with position 'absolute' or 'fixed' for which we are the
/// the CB. This is in tree order. This includes any direct children. /// containing block. This is in tree order. This includes any direct children.
abs_descendants: AbsDescendants, abs_descendants: AbsDescendants,
/// Details about descendants with position 'fixed'.
/// TODO: Optimize this, because this will be set only for the root.
fixed_descendants: FixedDescendants,
/// Offset wrt the nearest positioned ancestor - aka the Containing Block /// Offset wrt the nearest positioned ancestor - aka the Containing Block
/// for any absolutely positioned elements. /// for any absolutely positioned elements.
@ -800,8 +833,7 @@ impl BaseFlow {
next_sibling: None, next_sibling: None,
prev_sibling: Rawlink::none(), prev_sibling: Rawlink::none(),
min_width: Au::new(0), intrinsic_widths: IntrinsicWidths::new(),
pref_width: Au::new(0),
position: Au::zero_rect(), position: Au::zero_rect(),
overflow: Au::zero_rect(), overflow: Au::zero_rect(),
@ -809,9 +841,10 @@ impl BaseFlow {
floats: Floats::new(), floats: Floats::new(),
num_floats: 0, num_floats: 0,
collapsible_margins: CollapsibleMargins::new(),
clear: clear::none,
abs_position: Point2D(Au::new(0), Au::new(0)), abs_position: Point2D(Au::new(0), Au::new(0)),
abs_descendants: Descendants::new(), abs_descendants: Descendants::new(),
fixed_descendants: Descendants::new(),
absolute_static_x_offset: Au::new(0), absolute_static_x_offset: Au::new(0),
fixed_static_x_offset: Au::new(0), fixed_static_x_offset: Au::new(0),
absolute_cb: Rawlink::none(), absolute_cb: Rawlink::none(),
@ -1068,6 +1101,7 @@ impl<'a> MutableFlowUtils for &'a mut Flow {
overflow = overflow.union(&kid_overflow) overflow = overflow.union(&kid_overflow)
} }
// FIXME(#2004, pcwalton): This is wrong for `position: fixed`.
for descendant_link in mut_base(self).abs_descendants.iter() { for descendant_link in mut_base(self).abs_descendants.iter() {
match descendant_link.resolve() { match descendant_link.resolve() {
Some(flow) => { Some(flow) => {
@ -1078,183 +1112,62 @@ impl<'a> MutableFlowUtils for &'a mut Flow {
None => fail!("empty Rawlink to a descendant") None => fail!("empty Rawlink to a descendant")
} }
} }
if self.is_root() {
for fixed_descendant_link in mut_base(self).fixed_descendants.iter() {
match fixed_descendant_link.resolve() {
Some(flow) => {
let mut kid_overflow = base(flow).overflow;
kid_overflow = kid_overflow.translate(&my_position.origin);
overflow = overflow.union(&kid_overflow)
}
None => fail!("empty Rawlink to a descendant")
}
}
}
} }
mut_base(self).overflow = overflow; mut_base(self).overflow = overflow;
} }
/// Push display items for current flow and its children onto `list`. /// Push display items for current flow and its descendants onto the appropriate display lists
/// of the given stacking context.
/// ///
/// For InlineFlow, add display items for all its boxes onto list`. /// Arguments:
/// For BlockFlow, add a ClipDisplayItemClass for itself and its children,
/// plus any other display items like border.
/// ///
/// `container_block_size`: Size of the Containing Block for the current /// * `stacking_context`: The parent stacking context that this flow belongs to and to which
/// flow. This is used for relative positioning (which resolves percentage /// display items will be added.
/// values for 'top', etc. after all Containing Block heights have been computed.) ///
/// `absolute_cb_abs_position`: Absolute position of the Containing Block /// * `builder`: The display list builder, which contains information used during the entire
/// for the flow if it is absolutely positioned. /// display list building pass.
fn build_display_lists<E:ExtraDisplayListData>( ///
self, /// * `info`: Per-flow display list building information.
builder: &DisplayListBuilder, fn build_display_list(self,
container_block_size: &Size2D<Au>, stacking_context: &mut StackingContext,
absolute_cb_abs_position: Point2D<Au>, builder: &mut DisplayListBuilder,
dirty: &Rect<Au>, info: &DisplayListBuildingInfo) {
mut index: uint,
lists: &RefCell<DisplayListCollection<E>>)
-> bool {
debug!("Flow: building display list"); debug!("Flow: building display list");
index = match self.class() { match self.class() {
BlockFlowClass => self.as_block().build_display_list_block(builder, BlockFlowClass => {
container_block_size, self.as_block().build_display_list_block(stacking_context, builder, info)
absolute_cb_abs_position, }
dirty, InlineFlowClass => {
index, self.as_inline().build_display_list_inline(stacking_context, builder, info)
lists), }
InlineFlowClass => self.as_inline().build_display_list_inline(builder, container_block_size, dirty, index, lists), TableWrapperFlowClass => {
TableWrapperFlowClass => self.as_table_wrapper().build_display_list_table_wrapper(builder, self.as_table_wrapper().build_display_list_table_wrapper(stacking_context,
container_block_size, builder,
absolute_cb_abs_position, info)
dirty, }
index, TableFlowClass => {
lists), self.as_table().build_display_list_table(stacking_context, builder, info)
TableFlowClass => self.as_table().build_display_list_table(builder, }
container_block_size, TableRowGroupFlowClass => {
absolute_cb_abs_position, self.as_table_rowgroup().build_display_list_table_rowgroup(stacking_context,
dirty, builder,
index, info)
lists), }
TableRowGroupFlowClass => self.as_table_rowgroup().build_display_list_table_rowgroup(builder, TableRowFlowClass => {
container_block_size, self.as_table_row().build_display_list_table_row(stacking_context, builder, info)
absolute_cb_abs_position, }
dirty, TableCaptionFlowClass => {
index, self.as_table_caption().build_display_list_table_caption(stacking_context,
lists), builder,
TableRowFlowClass => self.as_table_row().build_display_list_table_row(builder, info)
container_block_size, }
absolute_cb_abs_position, TableCellFlowClass => {
dirty, self.as_table_cell().build_display_list_table_cell(stacking_context, builder, info)
index, }
lists), TableColGroupFlowClass => {
TableCaptionFlowClass => self.as_table_caption().build_display_list_table_caption(builder, // Nothing to do here, as column groups don't render.
container_block_size, }
absolute_cb_abs_position,
dirty,
index,
lists),
TableCellFlowClass => self.as_table_cell().build_display_list_table_cell(builder,
container_block_size,
absolute_cb_abs_position,
dirty,
index,
lists),
TableColGroupFlowClass => index,
};
if lists.with_mut(|lists| lists.lists[index].list.len() == 0) {
return true;
} }
if self.is_block_container() || self.is_table_kind() {
let block = self.as_block();
let mut child_lists = DisplayListCollection::new();
child_lists.add_list(DisplayList::new());
let child_lists = RefCell::new(child_lists);
let container_block_size;
let abs_cb_position;
// TODO(pradeep): Move this into a generated CB function and stuff in Flow.
match block.box_ {
Some(ref box_) => {
// The Containing Block formed by a Block for relatively
// positioned descendants is the content box.
container_block_size = box_.content_box_size();
abs_cb_position = if block.is_positioned() {
block.base.abs_position + block.generated_cb_position()
} else {
absolute_cb_abs_position
};
}
None => fail!("Flow: block container should have a box_")
}
for kid in block.base.child_iter() {
if kid.is_absolutely_positioned() {
// All absolute flows will be handled by their CB.
continue;
}
kid.build_display_lists(builder, &container_block_size,
abs_cb_position,
dirty, 0u, &child_lists);
}
// TODO: Maybe we should handle position 'absolute' and 'fixed'
// descendants before normal descendants just in case there is a
// problem when display-list building is parallel and both the
// original parent and this flow access the same absolute flow.
// Note that this can only be done once we have paint order
// working cos currently the later boxes paint over the absolute
// and fixed boxes :|
for abs_descendant_link in block.base.abs_descendants.iter() {
match abs_descendant_link.resolve() {
Some(flow) => {
// TODO(pradeep): Send in your abs_position directly.
flow.build_display_lists(builder, &container_block_size,
abs_cb_position,
dirty, 0u, &child_lists);
}
None => fail!("empty Rawlink to a descendant")
}
}
if block.is_root() {
for fixed_descendant_link in block.base.fixed_descendants.iter() {
match fixed_descendant_link.resolve() {
Some(flow) => {
flow.build_display_lists(builder, &container_block_size,
abs_cb_position,
dirty, 0u, &child_lists);
}
None => fail!("empty Rawlink to a descendant")
}
}
}
let mut child_lists = Some(child_lists.unwrap());
// Find parent ClipDisplayItemClass and push all child display items
// under it
lists.with_mut(|lists| {
let mut child_lists = child_lists.take_unwrap();
let result = lists.lists[index].list.mut_rev_iter().position(|item| {
match *item {
ClipDisplayItemClass(ref mut item) => {
item.child_list.push_all_move(child_lists.lists.shift().unwrap().list);
true
},
_ => false,
}
});
if result.is_none() {
fail!("fail to find parent item");
}
lists.lists.push_all_move(child_lists.lists);
});
}
true
} }
/// Destroys the flow. /// Destroys the flow.
@ -1314,29 +1227,6 @@ impl MutableOwnedFlowUtils for ~Flow {
} }
} }
/// Set fixed descendants for this flow.
///
/// Set yourself as the Containing Block for all the fixed descendants.
///
/// Assumption: This is called in a bottom-up traversal, so that nothing
/// else is accessing the descendant flows.
/// Assumption: This is the root flow.
fn set_fixed_descendants(&mut self, fixed_descendants: FixedDescendants) {
let self_link = Rawlink::some(*self);
let block = self.as_block();
block.base.fixed_descendants = fixed_descendants;
for descendant_link in block.base.fixed_descendants.iter() {
match descendant_link.resolve() {
Some(flow) => {
let base = mut_base(flow);
base.absolute_cb = self_link.clone();
}
None => fail!("empty Rawlink to a descendant")
}
}
}
/// Destroys the flow. /// Destroys the flow.
fn destroy(&mut self) { fn destroy(&mut self) {
let self_borrowed: &mut Flow = *self; let self_borrowed: &mut Flow = *self;

View file

@ -3,23 +3,24 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use css::node_style::StyledNode; use css::node_style::StyledNode;
use layout::box_::{Box, CannotSplit, GenericBox, IframeBox, ImageBox, ScannedTextBox, SplitDidFit}; use layout::box_::{Box, CannotSplit, GenericBox, IframeBox, ImageBox, InlineInfo, ScannedTextBox};
use layout::box_::{SplitDidNotFit, UnscannedTextBox, InlineInfo}; use layout::box_::{SplitDidFit, SplitDidNotFit, TableBox, TableCellBox, TableColumnBox};
use layout::box_::{TableColumnBox, TableRowBox, TableWrapperBox, TableCellBox, TableBox}; use layout::box_::{TableRowBox, TableWrapperBox, UnscannedTextBox};
use layout::context::LayoutContext; use layout::context::LayoutContext;
use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData}; use layout::display_list_builder::{DisplayListBuilder, DisplayListBuildingInfo};
use layout::floats::{FloatLeft, Floats, PlacementInfo}; use layout::floats::{FloatLeft, Floats, PlacementInfo};
use layout::flow::{BaseFlow, FlowClass, Flow, InlineFlowClass}; use layout::flow::{BaseFlow, FlowClass, Flow, InlineFlowClass};
use layout::flow; use layout::flow;
use layout::model::IntrinsicWidths;
use layout::util::ElementMapping; use layout::util::ElementMapping;
use layout::wrapper::ThreadSafeLayoutNode; use layout::wrapper::ThreadSafeLayoutNode;
use collections::{Deque, RingBuf}; use collections::{Deque, RingBuf};
use geom::{Point2D, Rect, Size2D}; use geom::{Point2D, Rect, Size2D};
use gfx::display_list::DisplayListCollection; use gfx::display_list::{ContentLevel, StackingContext};
use servo_util::geometry::Au; use servo_util::geometry::Au;
use servo_util::geometry;
use servo_util::range::Range; use servo_util::range::Range;
use std::cell::RefCell;
use std::mem; use std::mem;
use std::u16; use std::u16;
use style::computed_values::{text_align, vertical_align, white_space}; use style::computed_values::{text_align, vertical_align, white_space};
@ -480,17 +481,13 @@ impl InlineFlow {
self.boxes = ~[]; self.boxes = ~[];
} }
pub fn build_display_list_inline<E:ExtraDisplayListData>( pub fn build_display_list_inline(&self,
&self, stacking_context: &mut StackingContext,
builder: &DisplayListBuilder, builder: &DisplayListBuilder,
container_block_size: &Size2D<Au>, info: &DisplayListBuildingInfo) {
dirty: &Rect<Au>,
index: uint,
lists: &RefCell<DisplayListCollection<E>>)
-> uint {
let abs_rect = Rect(self.base.abs_position, self.base.position.size); let abs_rect = Rect(self.base.abs_position, self.base.position.size);
if !abs_rect.intersects(dirty) { if !abs_rect.intersects(&builder.dirty) {
return index; return
} }
// TODO(#228): Once we form line boxes and have their cached bounds, we can be smarter and // TODO(#228): Once we form line boxes and have their cached bounds, we can be smarter and
@ -498,15 +495,19 @@ impl InlineFlow {
debug!("Flow: building display list for {:u} inline boxes", self.boxes.len()); debug!("Flow: building display list for {:u} inline boxes", self.boxes.len());
for box_ in self.boxes.iter() { for box_ in self.boxes.iter() {
let rel_offset: Point2D<Au> = box_.relative_position(container_block_size); let rel_offset = box_.relative_position(&info.relative_containing_block_size);
box_.build_display_list(builder, dirty, self.base.abs_position + rel_offset, (&*self) as &Flow, index, lists); box_.build_display_list(stacking_context,
builder,
info,
self.base.abs_position + rel_offset,
(&*self) as &Flow,
ContentLevel);
} }
// TODO(#225): Should `inline-block` elements have flows as children of the inline flow or // TODO(#225): Should `inline-block` elements have flows as children of the inline flow or
// should the flow be nested inside the box somehow? // should the flow be nested inside the box somehow?
// For now, don't traverse the subtree rooted here // For now, don't traverse the subtree rooted here.
index
} }
/// Returns the relative offset from the baseline for this box, taking into account the value /// Returns the relative offset from the baseline for this box, taking into account the value
@ -628,20 +629,19 @@ impl Flow for InlineFlow {
child_base.floats = Floats::new(); child_base.floats = Floats::new();
} }
let mut min_width = Au::new(0); let mut intrinsic_widths = IntrinsicWidths::new();
let mut pref_width = Au::new(0);
for box_ in self.boxes.iter() { for box_ in self.boxes.iter() {
debug!("Flow: measuring {:s}", box_.debug_str()); debug!("Flow: measuring {:s}", box_.debug_str());
box_.compute_borders(box_.style()); box_.compute_borders(box_.style());
let (this_minimum_width, this_preferred_width) =
box_.minimum_and_preferred_widths(); let box_intrinsic_widths = box_.intrinsic_widths();
min_width = Au::max(min_width, this_minimum_width); intrinsic_widths.minimum_width = geometry::max(intrinsic_widths.minimum_width,
pref_width = Au::max(pref_width, this_preferred_width); box_intrinsic_widths.minimum_width);
intrinsic_widths.preferred_width = geometry::max(intrinsic_widths.preferred_width,
box_intrinsic_widths.preferred_width);
} }
self.base.min_width = min_width; self.base.intrinsic_widths = intrinsic_widths;
self.base.pref_width = pref_width;
self.base.num_floats = num_floats; self.base.num_floats = num_floats;
} }
@ -696,6 +696,7 @@ impl Flow for InlineFlow {
// //
// TODO(pcwalton): Cache the linebox scanner? // TODO(pcwalton): Cache the linebox scanner?
debug!("assign_height_inline: floats in: {:?}", self.base.floats); debug!("assign_height_inline: floats in: {:?}", self.base.floats);
// assign height for inline boxes // assign height for inline boxes
for box_ in self.boxes.iter() { for box_ in self.boxes.iter() {
box_.assign_replaced_height_if_necessary(); box_.assign_replaced_height_if_necessary();
@ -883,20 +884,6 @@ impl Flow for InlineFlow {
self.base.floats.translate(Point2D(Au::new(0), -self.base.position.size.height)); self.base.floats.translate(Point2D(Au::new(0), -self.base.position.size.height));
} }
fn collapse_margins(&mut self,
_: bool,
_: &mut bool,
_: &mut Au,
_: &mut Au,
collapsing: &mut Au,
collapsible: &mut Au) {
*collapsing = Au::new(0);
// Non-empty inline flows prevent collapsing between the previous margion and the next.
if self.base.position.size.height > Au::new(0) {
*collapsible = Au::new(0);
}
}
fn debug_str(&self) -> ~str { fn debug_str(&self) -> ~str {
~"InlineFlow: " + self.boxes.map(|s| s.debug_str()).connect(", ") ~"InlineFlow: " + self.boxes.map(|s| s.debug_str()).connect(", ")
} }

View file

@ -11,22 +11,22 @@ use css::select::new_stylist;
use css::node_style::StyledNode; use css::node_style::StyledNode;
use layout::construct::{FlowConstructionResult, NoConstructionResult}; use layout::construct::{FlowConstructionResult, NoConstructionResult};
use layout::context::LayoutContext; use layout::context::LayoutContext;
use layout::display_list_builder::{DisplayListBuilder, ToGfxColor}; use layout::display_list_builder::{DisplayListBuilder, DisplayListBuildingInfo, ToGfxColor};
use layout::flow::{Flow, ImmutableFlowUtils, MutableFlowUtils, MutableOwnedFlowUtils}; use layout::flow::{Flow, ImmutableFlowUtils, MutableFlowUtils, MutableOwnedFlowUtils};
use layout::flow::{PreorderFlowTraversal, PostorderFlowTraversal}; use layout::flow::{PreorderFlowTraversal, PostorderFlowTraversal};
use layout::flow; use layout::flow;
use layout::incremental::RestyleDamage; use layout::incremental::RestyleDamage;
use layout::parallel::PaddedUnsafeFlow; use layout::parallel::PaddedUnsafeFlow;
use layout::parallel; use layout::parallel;
use layout::util::{LayoutDataAccess, OpaqueNode, LayoutDataWrapper}; use layout::util::{LayoutDataAccess, LayoutDataWrapper, OpaqueNodeMethods};
use layout::wrapper::{LayoutNode, TLayoutNode, ThreadSafeLayoutNode}; use layout::wrapper::{LayoutNode, TLayoutNode, ThreadSafeLayoutNode};
use extra::url::Url; use extra::url::Url;
use geom::point::Point2D; use geom::point::Point2D;
use geom::rect::Rect; use geom::rect::Rect;
use geom::size::Size2D; use geom::size::Size2D;
use gfx::display_list::{ClipDisplayItemClass, DisplayItem, DisplayItemIterator}; use gfx::display_list::{ClipDisplayItemClass, DisplayItem, DisplayItemIterator, DisplayList};
use gfx::display_list::{DisplayList, DisplayListCollection}; use gfx::display_list::{OpaqueNode, StackingContext};
use gfx::font_context::{FontContext, FontContextInfo}; use gfx::font_context::{FontContext, FontContextInfo};
use gfx::render_task::{RenderMsg, RenderChan, RenderLayer}; use gfx::render_task::{RenderMsg, RenderChan, RenderLayer};
use gfx::{render_task, color}; use gfx::{render_task, color};
@ -41,18 +41,20 @@ use script::layout_interface::{ContentChangedDocumentDamage, LayoutChan, Msg, Pr
use script::layout_interface::{QueryMsg, ReapLayoutDataMsg, Reflow, UntrustedNodeAddress}; use script::layout_interface::{QueryMsg, ReapLayoutDataMsg, Reflow, UntrustedNodeAddress};
use script::layout_interface::{ReflowForDisplay, ReflowMsg}; use script::layout_interface::{ReflowForDisplay, ReflowMsg};
use script::script_task::{ReflowCompleteMsg, ScriptChan, SendEventMsg}; use script::script_task::{ReflowCompleteMsg, ScriptChan, SendEventMsg};
use servo_msg::compositor_msg::Scrollable;
use servo_msg::constellation_msg::{ConstellationChan, PipelineId, Failure, FailureMsg}; use servo_msg::constellation_msg::{ConstellationChan, PipelineId, Failure, FailureMsg};
use servo_net::image_cache_task::{ImageCacheTask, ImageResponseMsg}; use servo_net::image_cache_task::{ImageCacheTask, ImageResponseMsg};
use servo_net::local_image_cache::{ImageResponder, LocalImageCache}; use servo_net::local_image_cache::{ImageResponder, LocalImageCache};
use servo_util::geometry::Au; use servo_util::geometry::Au;
use servo_util::geometry;
use servo_util::opts::Opts; use servo_util::opts::Opts;
use servo_util::smallvec::{SmallVec, SmallVec0, SmallVec1};
use servo_util::time::{ProfilerChan, profile}; use servo_util::time::{ProfilerChan, profile};
use servo_util::time; use servo_util::time;
use servo_util::task::send_on_failure; use servo_util::task::send_on_failure;
use servo_util::workqueue::WorkQueue; use servo_util::workqueue::WorkQueue;
use std::cast::transmute; use std::cast::transmute;
use std::cast; use std::cast;
use std::cell::RefCell;
use std::comm::Port; use std::comm::Port;
use std::mem; use std::mem;
use std::ptr; use std::ptr;
@ -79,7 +81,7 @@ pub struct LayoutTask {
script_chan: ScriptChan, script_chan: ScriptChan,
/// The channel on which messages can be sent to the painting task. /// The channel on which messages can be sent to the painting task.
render_chan: RenderChan<OpaqueNode>, render_chan: RenderChan,
/// The channel on which messages can be sent to the image cache. /// The channel on which messages can be sent to the image cache.
image_cache_task: ImageCacheTask, image_cache_task: ImageCacheTask,
@ -91,7 +93,7 @@ pub struct LayoutTask {
screen_size: Size2D<Au>, screen_size: Size2D<Au>,
/// A cached display list. /// A cached display list.
display_list_collection: Option<Arc<DisplayListCollection<OpaqueNode>>>, display_list: Option<Arc<DisplayList>>,
stylist: ~Stylist, stylist: ~Stylist,
@ -251,7 +253,7 @@ impl LayoutTask {
constellation_chan: ConstellationChan, constellation_chan: ConstellationChan,
failure_msg: Failure, failure_msg: Failure,
script_chan: ScriptChan, script_chan: ScriptChan,
render_chan: RenderChan<OpaqueNode>, render_chan: RenderChan,
img_cache_task: ImageCacheTask, img_cache_task: ImageCacheTask,
opts: Opts, opts: Opts,
profiler_chan: ProfilerChan, profiler_chan: ProfilerChan,
@ -282,7 +284,7 @@ impl LayoutTask {
chan: LayoutChan, chan: LayoutChan,
constellation_chan: ConstellationChan, constellation_chan: ConstellationChan,
script_chan: ScriptChan, script_chan: ScriptChan,
render_chan: RenderChan<OpaqueNode>, render_chan: RenderChan,
image_cache_task: ImageCacheTask, image_cache_task: ImageCacheTask,
opts: &Opts, opts: &Opts,
profiler_chan: ProfilerChan) profiler_chan: ProfilerChan)
@ -306,7 +308,7 @@ impl LayoutTask {
local_image_cache: local_image_cache, local_image_cache: local_image_cache,
screen_size: screen_size, screen_size: screen_size,
display_list_collection: None, display_list: None,
stylist: ~new_stylist(), stylist: ~new_stylist(),
initial_css_values: Arc::new(style::initial_values()), initial_css_values: Arc::new(style::initial_values()),
parallel_traversal: parallel_traversal, parallel_traversal: parallel_traversal,
@ -339,7 +341,7 @@ impl LayoutTask {
stylist: &*self.stylist, stylist: &*self.stylist,
initial_css_values: self.initial_css_values.clone(), initial_css_values: self.initial_css_values.clone(),
url: (*url).clone(), url: (*url).clone(),
reflow_root: OpaqueNode::from_layout_node(reflow_root), reflow_root: OpaqueNodeMethods::from_layout_node(reflow_root),
opts: self.opts.clone(), opts: self.opts.clone(),
} }
} }
@ -432,15 +434,13 @@ impl LayoutTask {
None => fail!("no layout data for root node"), None => fail!("no layout data for root node"),
}; };
let mut flow = match result { let mut flow = match result {
FlowConstructionResult(mut flow, abs_descendants, fixed_descendants) => { FlowConstructionResult(mut flow, abs_descendants) => {
// Note: Assuming that the root has display 'static' (as per // Note: Assuming that the root has display 'static' (as per
// CSS Section 9.3.1). Otherwise, if it were absolutely // CSS Section 9.3.1). Otherwise, if it were absolutely
// positioned, it would return a reference to itself in // positioned, it would return a reference to itself in
// `abs_descendants` and would lead to a circular reference. // `abs_descendants` and would lead to a circular reference.
// Set Root as CB for any remaining absolute descendants. // Set Root as CB for any remaining absolute descendants.
flow.set_abs_descendants(abs_descendants); flow.set_abs_descendants(abs_descendants);
// Set Root as CB for all fixed descendants.
flow.set_fixed_descendants(fixed_descendants);
flow flow
} }
_ => fail!("Flow construction didn't result in a flow at the root of the tree!"), _ => fail!("Flow construction didn't result in a flow at the root of the tree!"),
@ -627,26 +627,30 @@ impl LayoutTask {
// Build the display list if necessary, and send it to the renderer. // Build the display list if necessary, and send it to the renderer.
if data.goal == ReflowForDisplay { if data.goal == ReflowForDisplay {
profile(time::LayoutDispListBuildCategory, self.profiler_chan.clone(), || { profile(time::LayoutDispListBuildCategory, self.profiler_chan.clone(), || {
let root_size = flow::base(layout_root).position.size; let mut root_stacking_context = StackingContext::new();
let root_abs_position = Point2D(Au::new(0), Au::new(0)); let mut display_list_builder = DisplayListBuilder {
let mut display_list_collection = DisplayListCollection::new();
display_list_collection.add_list(DisplayList::<OpaqueNode>::new());
let display_list_collection = ~RefCell::new(display_list_collection);
let dirty = flow::base(layout_root).position.clone();
let display_list_builder = DisplayListBuilder {
ctx: &layout_ctx, ctx: &layout_ctx,
layers: SmallVec0::new(),
dirty: flow::base(layout_root).position.clone(),
};
let display_list_building_info = DisplayListBuildingInfo {
relative_containing_block_size: flow::base(layout_root).position.size,
absolute_containing_block_position: Point2D(Au(0), Au(0)),
layers_needed_for_positioned_flows: false,
}; };
layout_root.build_display_lists(&display_list_builder, &root_size,
root_abs_position,
&dirty, 0u, display_list_collection);
let display_list_collection = Arc::new(display_list_collection.unwrap()); layout_root.build_display_list(&mut root_stacking_context,
&mut display_list_builder,
&display_list_building_info);
let display_list = Arc::new(root_stacking_context.flatten());
// FIXME(pcwalton): This is really ugly and can't handle overflow: scroll. Refactor
// it with extreme prejudice.
let mut color = color::rgba(255.0, 255.0, 255.0, 255.0); let mut color = color::rgba(255.0, 255.0, 255.0, 255.0);
for child in node.traverse_preorder() { for child in node.traverse_preorder() {
if child.type_id() == ElementNodeTypeId(HTMLHtmlElementTypeId) || if child.type_id() == Some(ElementNodeTypeId(HTMLHtmlElementTypeId)) ||
child.type_id() == ElementNodeTypeId(HTMLBodyElementTypeId) { child.type_id() == Some(ElementNodeTypeId(HTMLBodyElementTypeId)) {
let element_bg_color = { let element_bg_color = {
let thread_safe_child = ThreadSafeLayoutNode::new(&child); let thread_safe_child = ThreadSafeLayoutNode::new(&child);
thread_safe_child.style() thread_safe_child.style()
@ -668,18 +672,33 @@ impl LayoutTask {
} }
} }
let root_size = Size2D(display_list_building_info.relative_containing_block_size
.width
.to_nearest_px() as uint,
display_list_building_info.relative_containing_block_size
.height
.to_nearest_px() as uint);
let render_layer = RenderLayer { let render_layer = RenderLayer {
display_list_collection: display_list_collection.clone(), id: layout_root.layer_id(0),
size: Size2D(root_size.width.to_nearest_px() as uint, display_list: display_list.clone(),
root_size.height.to_nearest_px() as uint), position: Rect(Point2D(0u, 0u), root_size),
color: color background_color: color,
scroll_policy: Scrollable,
}; };
self.display_list_collection = Some(display_list_collection.clone()); self.display_list = Some(display_list.clone());
let mut layers = SmallVec1::new();
layers.push(render_layer);
let DisplayListBuilder {
layers: sublayers,
..
} = display_list_builder;
layers.push_all_move(sublayers);
debug!("Layout done!"); debug!("Layout done!");
self.render_chan.send(RenderMsg(render_layer)); self.render_chan.send(RenderMsg(layers));
}); });
} }
@ -701,15 +720,14 @@ impl LayoutTask {
// The neat thing here is that in order to answer the following two queries we only // The neat thing here is that in order to answer the following two queries we only
// need to compare nodes for equality. Thus we can safely work only with `OpaqueNode`. // need to compare nodes for equality. Thus we can safely work only with `OpaqueNode`.
ContentBoxQuery(node, reply_chan) => { ContentBoxQuery(node, reply_chan) => {
let node = OpaqueNode::from_script_node(node); let node: OpaqueNode = OpaqueNodeMethods::from_script_node(node);
fn union_boxes_for_node<'a>( fn union_boxes_for_node(accumulator: &mut Option<Rect<Au>>,
accumulator: &mut Option<Rect<Au>>, mut iter: DisplayItemIterator,
mut iter: DisplayItemIterator<'a,OpaqueNode>,
node: OpaqueNode) { node: OpaqueNode) {
for item in iter { for item in iter {
union_boxes_for_node(accumulator, item.children(), node); union_boxes_for_node(accumulator, item.children(), node);
if item.base().extra == node { if item.base().node == node {
match *accumulator { match *accumulator {
None => *accumulator = Some(item.base().bounds), None => *accumulator = Some(item.base().bounds),
Some(ref mut acc) => *acc = acc.union(&item.base().bounds), Some(ref mut acc) => *acc = acc.union(&item.base().bounds),
@ -719,41 +737,49 @@ impl LayoutTask {
} }
let mut rect = None; let mut rect = None;
for display_list in self.display_list_collection.as_ref().unwrap().get().iter() { match self.display_list {
union_boxes_for_node(&mut rect, display_list.iter(), node); None => fail!("no display list!"),
Some(ref display_list) => {
union_boxes_for_node(&mut rect, display_list.get().iter(), node)
}
} }
reply_chan.send(ContentBoxResponse(rect.unwrap_or(Au::zero_rect()))) reply_chan.send(ContentBoxResponse(rect.unwrap_or(Au::zero_rect())))
} }
ContentBoxesQuery(node, reply_chan) => { ContentBoxesQuery(node, reply_chan) => {
let node = OpaqueNode::from_script_node(node); let node: OpaqueNode = OpaqueNodeMethods::from_script_node(node);
fn add_boxes_for_node<'a>( fn add_boxes_for_node(accumulator: &mut ~[Rect<Au>],
accumulator: &mut ~[Rect<Au>], mut iter: DisplayItemIterator,
mut iter: DisplayItemIterator<'a,OpaqueNode>,
node: OpaqueNode) { node: OpaqueNode) {
for item in iter { for item in iter {
add_boxes_for_node(accumulator, item.children(), node); add_boxes_for_node(accumulator, item.children(), node);
if item.base().extra == node { if item.base().node == node {
accumulator.push(item.base().bounds) accumulator.push(item.base().bounds)
} }
} }
} }
let mut boxes = ~[]; let mut boxes = ~[];
for display_list in self.display_list_collection.as_ref().unwrap().get().iter() { match self.display_list {
add_boxes_for_node(&mut boxes, display_list.iter(), node); None => fail!("no display list!"),
Some(ref display_list) => {
add_boxes_for_node(&mut boxes, display_list.get().iter(), node)
}
} }
reply_chan.send(ContentBoxesResponse(boxes)) reply_chan.send(ContentBoxesResponse(boxes))
} }
HitTestQuery(_, point, reply_chan) => { HitTestQuery(_, point, reply_chan) => {
fn hit_test(x: Au, y: Au, list: &[DisplayItem<OpaqueNode>]) fn hit_test(x: Au, y: Au, list: &[DisplayItem])
-> Option<HitTestResponse> { -> Option<HitTestResponse> {
for item in list.rev_iter() { for item in list.rev_iter() {
match *item { match *item {
ClipDisplayItemClass(ref cc) => { ClipDisplayItemClass(ref cc) => {
let ret = hit_test(x, y, cc.child_list); if !cc.need_clip || geometry::rect_contains_point(cc.base.bounds,
if !ret.is_none() { Point2D(x, y)) {
return ret; let ret = hit_test(x, y, cc.child_list.as_slice());
if !ret.is_none() {
return ret
}
} }
} }
_ => {} _ => {}
@ -774,31 +800,35 @@ impl LayoutTask {
y < bounds.origin.y + bounds.size.height && y < bounds.origin.y + bounds.size.height &&
bounds.origin.y <= y { bounds.origin.y <= y {
return Some(HitTestResponse(item.base() return Some(HitTestResponse(item.base()
.extra .node
.to_untrusted_node_address())) .to_untrusted_node_address()))
} }
} }
let ret: Option<HitTestResponse> = None; let ret: Option<HitTestResponse> = None;
ret ret
} }
for display_list in self.display_list_collection.as_ref().unwrap().get().lists.rev_iter() { let (x, y) = (Au::from_frac_px(point.x as f64),
let (x, y) = (Au::from_frac_px(point.x as f64), Au::from_frac_px(point.y as f64));
Au::from_frac_px(point.y as f64)); let resp = match self.display_list {
let resp = hit_test(x,y,display_list.list); None => fail!("no display list!"),
if resp.is_some() { Some(ref display_list) => hit_test(x, y, display_list.get().list.as_slice()),
reply_chan.send(Ok(resp.unwrap())); };
return if resp.is_some() {
} reply_chan.send(Ok(resp.unwrap()));
return
} }
reply_chan.send(Err(())); reply_chan.send(Err(()));
} }
MouseOverQuery(_, point, reply_chan) => { MouseOverQuery(_, point, reply_chan) => {
fn mouse_over_test(x: Au, y: Au, list: &[DisplayItem<OpaqueNode>], result: &mut ~[UntrustedNodeAddress]) { fn mouse_over_test(x: Au,
y: Au,
list: &[DisplayItem],
result: &mut ~[UntrustedNodeAddress]) {
for item in list.rev_iter() { for item in list.rev_iter() {
match *item { match *item {
ClipDisplayItemClass(ref cc) => { ClipDisplayItemClass(ref cc) => {
mouse_over_test(x, y, cc.child_list, result); mouse_over_test(x, y, cc.child_list.as_slice(), result);
} }
_ => {} _ => {}
} }
@ -814,18 +844,24 @@ impl LayoutTask {
y < bounds.origin.y + bounds.size.height && y < bounds.origin.y + bounds.size.height &&
bounds.origin.y <= y { bounds.origin.y <= y {
result.push(item.base() result.push(item.base()
.extra .node
.to_untrusted_node_address()); .to_untrusted_node_address());
} }
} }
} }
let mut mouse_over_list:~[UntrustedNodeAddress] = ~[]; let mut mouse_over_list:~[UntrustedNodeAddress] = ~[];
for display_list in self.display_list_collection.as_ref().unwrap().get().lists.rev_iter() { let (x, y) = (Au::from_frac_px(point.x as f64),
let (x, y) = (Au::from_frac_px(point.x as f64), Au::from_frac_px(point.y as f64));
Au::from_frac_px(point.y as f64)); match self.display_list {
mouse_over_test(x,y,display_list.list, &mut mouse_over_list); None => fail!("no display list!"),
} Some(ref display_list) => {
mouse_over_test(x,
y,
display_list.get().list.as_slice(),
&mut mouse_over_list);
}
};
if mouse_over_list.is_empty() { if mouse_over_list.is_empty() {
reply_chan.send(Err(())); reply_chan.send(Err(()));

View file

@ -4,8 +4,257 @@
//! Borders, padding, and margins. //! Borders, padding, and margins.
use servo_util::geometry::Au; use layout::box_::Box;
use computed = style::computed_values; use computed = style::computed_values;
use style::computed_values::{LPA_Auto, LPA_Length, LPA_Percentage};
use servo_util::geometry::Au;
use servo_util::geometry;
/// A collapsible margin. See CSS 2.1 § 8.3.1.
pub struct AdjoiningMargins {
/// The value of the greatest positive margin.
most_positive: Au,
/// The actual value (not the absolute value) of the negative margin with the largest absolute
/// value. Since this is not the absolute value, this is always zero or negative.
most_negative: Au,
}
impl AdjoiningMargins {
pub fn new() -> AdjoiningMargins {
AdjoiningMargins {
most_positive: Au(0),
most_negative: Au(0),
}
}
pub fn from_margin(margin_value: Au) -> AdjoiningMargins {
if margin_value >= Au(0) {
AdjoiningMargins {
most_positive: margin_value,
most_negative: Au(0),
}
} else {
AdjoiningMargins {
most_positive: Au(0),
most_negative: margin_value,
}
}
}
pub fn union(&mut self, other: AdjoiningMargins) {
self.most_positive = geometry::max(self.most_positive, other.most_positive);
self.most_negative = geometry::min(self.most_negative, other.most_negative)
}
pub fn collapse(&self) -> Au {
self.most_positive + self.most_negative
}
}
/// Represents the top and bottom margins of a flow with collapsible margins. See CSS 2.1 § 8.3.1.
pub enum CollapsibleMargins {
/// Margins may not collapse with this flow.
NoCollapsibleMargins(Au, Au),
/// Both the top and bottom margins (specified here in that order) may collapse, but the
/// margins do not collapse through this flow.
MarginsCollapse(AdjoiningMargins, AdjoiningMargins),
/// Margins collapse *through* this flow. This means, essentially, that the flow doesnt
/// have any border, padding, or out-of-flow (floating or positioned) content
MarginsCollapseThrough(AdjoiningMargins),
}
impl CollapsibleMargins {
pub fn new() -> CollapsibleMargins {
NoCollapsibleMargins(Au(0), Au(0))
}
}
enum FinalMarginState {
MarginsCollapseThroughFinalMarginState,
BottomMarginCollapsesFinalMarginState,
}
pub struct MarginCollapseInfo {
state: MarginCollapseState,
top_margin: AdjoiningMargins,
margin_in: AdjoiningMargins,
}
impl MarginCollapseInfo {
/// TODO(#2012, pcwalton): Remove this method once `box_` is not an `Option`.
pub fn new() -> MarginCollapseInfo {
MarginCollapseInfo {
state: AccumulatingCollapsibleTopMargin,
top_margin: AdjoiningMargins::new(),
margin_in: AdjoiningMargins::new(),
}
}
pub fn initialize_top_margin(&mut self,
fragment: &Box,
can_collapse_top_margin_with_kids: bool) {
if !can_collapse_top_margin_with_kids {
self.state = AccumulatingMarginIn
}
self.top_margin = AdjoiningMargins::from_margin(fragment.margin.get().top)
}
pub fn finish_and_compute_collapsible_margins(mut self,
fragment: &Box,
can_collapse_bottom_margin_with_kids: bool)
-> (CollapsibleMargins, Au) {
let state = match self.state {
AccumulatingCollapsibleTopMargin => {
match fragment.style().Box.get().height {
LPA_Auto | LPA_Length(Au(0)) | LPA_Percentage(0.) => {
MarginsCollapseThroughFinalMarginState
},
_ => {
// If the box has an explicitly specified height, margins may not collapse
// through it.
BottomMarginCollapsesFinalMarginState
}
}
}
AccumulatingMarginIn => BottomMarginCollapsesFinalMarginState,
};
// Different logic is needed here depending on whether this flow can collapse its bottom
// margin with its children.
let bottom_margin = fragment.margin.get().bottom;
if !can_collapse_bottom_margin_with_kids {
match state {
MarginsCollapseThroughFinalMarginState => {
let advance = self.top_margin.collapse();
self.margin_in.union(AdjoiningMargins::from_margin(bottom_margin));
(MarginsCollapse(self.top_margin, self.margin_in), advance)
}
BottomMarginCollapsesFinalMarginState => {
let advance = self.margin_in.collapse();
self.margin_in.union(AdjoiningMargins::from_margin(bottom_margin));
(MarginsCollapse(self.top_margin, self.margin_in), advance)
}
}
} else {
match state {
MarginsCollapseThroughFinalMarginState => {
self.top_margin.union(AdjoiningMargins::from_margin(bottom_margin));
(MarginsCollapseThrough(self.top_margin), Au(0))
}
BottomMarginCollapsesFinalMarginState => {
self.margin_in.union(AdjoiningMargins::from_margin(bottom_margin));
(MarginsCollapse(self.top_margin, self.margin_in), Au(0))
}
}
}
}
pub fn current_float_ceiling(&mut self) -> Au {
match self.state {
AccumulatingCollapsibleTopMargin => self.top_margin.collapse(),
AccumulatingMarginIn => self.margin_in.collapse(),
}
}
/// Adds the child's potentially collapsible top margin to the current margin state and
/// advances the Y offset by the appropriate amount to handle that margin. Returns the amount
/// that should be added to the Y offset during block layout.
pub fn advance_top_margin(&mut self, child_collapsible_margins: &CollapsibleMargins) -> Au {
match (self.state, *child_collapsible_margins) {
(AccumulatingCollapsibleTopMargin, NoCollapsibleMargins(top, _)) => {
self.state = AccumulatingMarginIn;
top
}
(AccumulatingCollapsibleTopMargin, MarginsCollapse(top, _)) => {
self.top_margin.union(top);
self.state = AccumulatingMarginIn;
Au(0)
}
(AccumulatingMarginIn, NoCollapsibleMargins(top, _)) => {
let previous_margin_value = self.margin_in.collapse();
self.margin_in = AdjoiningMargins::new();
previous_margin_value + top
}
(AccumulatingMarginIn, MarginsCollapse(top, _)) => {
self.margin_in.union(top);
let margin_value = self.margin_in.collapse();
self.margin_in = AdjoiningMargins::new();
margin_value
}
(_, MarginsCollapseThrough(_)) => {
// For now, we ignore this; this will be handled by `advance_bottom_margin` below.
Au(0)
}
}
}
/// Adds the child's potentially collapsible bottom margin to the current margin state and
/// advances the Y offset by the appropriate amount to handle that margin. Returns the amount
/// that should be added to the Y offset during block layout.
pub fn advance_bottom_margin(&mut self, child_collapsible_margins: &CollapsibleMargins) -> Au {
match (self.state, *child_collapsible_margins) {
(AccumulatingCollapsibleTopMargin, NoCollapsibleMargins(..)) |
(AccumulatingCollapsibleTopMargin, MarginsCollapse(..)) => {
// Can't happen because the state will have been replaced with
// `AccumulatingMarginIn` above.
fail!("should not be accumulating collapsible top margins anymore!")
}
(AccumulatingCollapsibleTopMargin, MarginsCollapseThrough(margin)) => {
self.top_margin.union(margin);
Au(0)
}
(AccumulatingMarginIn, NoCollapsibleMargins(_, bottom)) => {
assert_eq!(self.margin_in.most_positive, Au(0));
assert_eq!(self.margin_in.most_negative, Au(0));
bottom
}
(AccumulatingMarginIn, MarginsCollapse(_, bottom)) |
(AccumulatingMarginIn, MarginsCollapseThrough(bottom)) => {
self.margin_in.union(bottom);
Au(0)
}
}
}
}
enum MarginCollapseState {
AccumulatingCollapsibleTopMargin,
AccumulatingMarginIn,
}
/// Intrinsic widths, which consist of minimum and preferred.
pub struct IntrinsicWidths {
/// The *minimum width* of the content.
minimum_width: Au,
/// The *preferred width* of the content.
preferred_width: Au,
/// The estimated sum of borders, padding, and margins. Some calculations use this information
/// when computing intrinsic widths.
surround_width: Au,
}
impl IntrinsicWidths {
pub fn new() -> IntrinsicWidths {
IntrinsicWidths {
minimum_width: Au(0),
preferred_width: Au(0),
surround_width: Au(0),
}
}
pub fn total_minimum_width(&self) -> Au {
self.minimum_width + self.surround_width
}
pub fn total_preferred_width(&self) -> Au {
self.preferred_width + self.surround_width
}
}
/// Useful helper data type when computing values for blocks and positioned elements. /// Useful helper data type when computing values for blocks and positioned elements.
pub enum MaybeAuto { pub enum MaybeAuto {

View file

@ -14,10 +14,11 @@ use layout::flow::{Flow, PreorderFlowTraversal, PostorderFlowTraversal};
use layout::flow; use layout::flow;
use layout::layout_task::{AssignHeightsAndStoreOverflowTraversal, AssignWidthsTraversal}; use layout::layout_task::{AssignHeightsAndStoreOverflowTraversal, AssignWidthsTraversal};
use layout::layout_task::{BubbleWidthsTraversal}; use layout::layout_task::{BubbleWidthsTraversal};
use layout::util::{LayoutDataAccess, OpaqueNode}; use layout::util::{LayoutDataAccess, OpaqueNodeMethods};
use layout::wrapper::{layout_node_to_unsafe_layout_node, LayoutNode, PostorderNodeMutTraversal}; use layout::wrapper::{layout_node_to_unsafe_layout_node, LayoutNode, PostorderNodeMutTraversal};
use layout::wrapper::{ThreadSafeLayoutNode, UnsafeLayoutNode}; use layout::wrapper::{ThreadSafeLayoutNode, UnsafeLayoutNode};
use gfx::display_list::OpaqueNode;
use servo_util::time::{ProfilerChan, profile}; use servo_util::time::{ProfilerChan, profile};
use servo_util::time; use servo_util::time;
use servo_util::workqueue::{WorkQueue, WorkUnit, WorkerProxy}; use servo_util::workqueue::{WorkQueue, WorkUnit, WorkerProxy};
@ -244,7 +245,8 @@ fn recalc_style_for_node(unsafe_layout_node: UnsafeLayoutNode,
node.initialize_layout_data(layout_context.layout_chan.clone()); node.initialize_layout_data(layout_context.layout_chan.clone());
// Get the parent node. // Get the parent node.
let parent_opt = if OpaqueNode::from_layout_node(&node) == layout_context.reflow_root { let opaque_node: OpaqueNode = OpaqueNodeMethods::from_layout_node(&node);
let parent_opt = if opaque_node == layout_context.reflow_root {
None None
} else { } else {
node.parent_node() node.parent_node()
@ -345,7 +347,8 @@ fn construct_flows(mut unsafe_layout_node: UnsafeLayoutNode,
} }
// If this is the reflow root, we're done. // If this is the reflow root, we're done.
if layout_context.reflow_root == OpaqueNode::from_layout_node(&node) { let opaque_node: OpaqueNode = OpaqueNodeMethods::from_layout_node(&node);
if layout_context.reflow_root == opaque_node {
break break
} }

View file

@ -5,22 +5,21 @@
//! CSS table formatting contexts. //! CSS table formatting contexts.
use layout::box_::Box; use layout::box_::Box;
use layout::block::BlockFlow; use layout::block::{BlockFlow, MarginsMayNotCollapse, WidthAndMarginsComputer};
use layout::block::{WidthAndMarginsComputer, WidthConstraintInput, WidthConstraintSolution}; use layout::block::{WidthConstraintInput, WidthConstraintSolution};
use layout::construct::FlowConstructor; use layout::construct::FlowConstructor;
use layout::context::LayoutContext; use layout::context::LayoutContext;
use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData}; use layout::display_list_builder::{DisplayListBuilder, DisplayListBuildingInfo};
use layout::floats::{FloatKind}; use layout::floats::{FloatKind};
use layout::flow::{TableFlowClass, FlowClass, Flow, ImmutableFlowUtils}; use layout::flow::{TableFlowClass, FlowClass, Flow, ImmutableFlowUtils};
use layout::flow; use layout::flow;
use layout::table_wrapper::{TableLayout, FixedLayout, AutoLayout}; use layout::table_wrapper::{TableLayout, FixedLayout, AutoLayout};
use layout::wrapper::ThreadSafeLayoutNode; use layout::wrapper::ThreadSafeLayoutNode;
use std::cell::RefCell; use gfx::display_list::StackingContext;
use style::computed_values::table_layout;
use geom::{Point2D, Rect, Size2D};
use gfx::display_list::DisplayListCollection;
use servo_util::geometry::Au; use servo_util::geometry::Au;
use servo_util::geometry;
use style::computed_values::table_layout;
/// A table flow corresponded to the table's internal table box under a table wrapper flow. /// A table flow corresponded to the table's internal table box under a table wrapper flow.
/// The properties `position`, `float`, and `margin-*` are used on the table wrapper box, /// The properties `position`, `float`, and `margin-*` are used on the table wrapper box,
@ -31,6 +30,12 @@ pub struct TableFlow {
/// Column widths /// Column widths
col_widths: ~[Au], col_widths: ~[Au],
/// Column min widths.
col_min_widths: ~[Au],
/// Column pref widths.
col_pref_widths: ~[Au],
/// Table-layout property /// Table-layout property
table_layout: TableLayout, table_layout: TableLayout,
} }
@ -49,6 +54,8 @@ impl TableFlow {
TableFlow { TableFlow {
block_flow: block_flow, block_flow: block_flow,
col_widths: ~[], col_widths: ~[],
col_min_widths: ~[],
col_pref_widths: ~[],
table_layout: table_layout table_layout: table_layout
} }
} }
@ -66,6 +73,8 @@ impl TableFlow {
TableFlow { TableFlow {
block_flow: block_flow, block_flow: block_flow,
col_widths: ~[], col_widths: ~[],
col_min_widths: ~[],
col_pref_widths: ~[],
table_layout: table_layout table_layout: table_layout
} }
} }
@ -84,6 +93,8 @@ impl TableFlow {
TableFlow { TableFlow {
block_flow: block_flow, block_flow: block_flow,
col_widths: ~[], col_widths: ~[],
col_min_widths: ~[],
col_pref_widths: ~[],
table_layout: table_layout table_layout: table_layout
} }
} }
@ -91,61 +102,46 @@ impl TableFlow {
pub fn teardown(&mut self) { pub fn teardown(&mut self) {
self.block_flow.teardown(); self.block_flow.teardown();
self.col_widths = ~[]; self.col_widths = ~[];
self.col_min_widths = ~[];
self.col_pref_widths = ~[];
}
/// Update the corresponding value of self_widths if a value of kid_widths has larger value
/// than one of self_widths.
pub fn update_col_widths(self_widths: &mut ~[Au], kid_widths: &~[Au]) -> Au {
let mut sum_widths = Au(0);
let mut kid_widths_it = kid_widths.iter();
for self_width in self_widths.mut_iter() {
match kid_widths_it.next() {
Some(kid_width) => {
if *self_width < *kid_width {
*self_width = *kid_width;
}
},
None => {}
}
sum_widths = sum_widths + *self_width;
}
sum_widths
} }
/// Assign height for table flow. /// Assign height for table flow.
/// ///
/// TODO(#2014, pcwalton): This probably doesn't handle margin collapse right.
///
/// inline(always) because this is only ever called by in-order or non-in-order top-level /// inline(always) because this is only ever called by in-order or non-in-order top-level
/// methods /// methods
#[inline(always)] #[inline(always)]
fn assign_height_table_base(&mut self, ctx: &mut LayoutContext, inorder: bool) { fn assign_height_table_base(&mut self, layout_context: &mut LayoutContext, inorder: bool) {
self.block_flow.assign_height_block_base(layout_context, inorder, MarginsMayNotCollapse);
let (_, top_offset, bottom_offset, left_offset) = self.block_flow.initialize_offsets(true);
self.block_flow.handle_children_floats_if_necessary(ctx, inorder,
left_offset, top_offset);
let mut cur_y = top_offset;
for kid in self.block_flow.base.child_iter() {
let child_node = flow::mut_base(kid);
child_node.position.origin.y = cur_y;
cur_y = cur_y + child_node.position.size.height;
}
let height = cur_y - top_offset;
let mut noncontent_height = Au::new(0);
for box_ in self.block_flow.box_.iter() {
let mut position = box_.border_box.get();
// noncontent_height = border_top/bottom + padding_top/bottom of box
noncontent_height = box_.noncontent_height();
position.origin.y = Au(0);
position.size.height = height + noncontent_height;
box_.border_box.set(position);
}
self.block_flow.base.position.size.height = height + noncontent_height;
self.block_flow.set_floats_out_if_inorder(inorder, height, cur_y,
top_offset, bottom_offset, left_offset);
} }
pub fn build_display_list_table<E:ExtraDisplayListData>( pub fn build_display_list_table(&mut self,
&mut self, stacking_context: &mut StackingContext,
builder: &DisplayListBuilder, builder: &mut DisplayListBuilder,
container_block_size: &Size2D<Au>, info: &DisplayListBuildingInfo) {
absolute_cb_abs_position: Point2D<Au>,
dirty: &Rect<Au>,
index: uint,
lists: &RefCell<DisplayListCollection<E>>)
-> uint {
debug!("build_display_list_table: same process as block flow"); debug!("build_display_list_table: same process as block flow");
self.block_flow.build_display_list_block(builder, container_block_size, self.block_flow.build_display_list_block(stacking_context, builder, info);
absolute_cb_abs_position,
dirty, index, lists)
} }
} }
@ -162,54 +158,95 @@ impl Flow for TableFlow {
&mut self.block_flow &mut self.block_flow
} }
/// This function finds the specified column widths from column group and the first row. fn col_widths<'a>(&'a mut self) -> &'a mut ~[Au] {
/// Those are used in fixed table layout calculation. &mut self.col_widths
/* FIXME: automatic table layout calculation */ }
fn bubble_widths(&mut self, ctx: &mut LayoutContext) {
let mut did_first_row = false; fn col_min_widths<'a>(&'a self) -> &'a ~[Au] {
&self.col_min_widths
}
fn col_pref_widths<'a>(&'a self) -> &'a ~[Au] {
&self.col_pref_widths
}
/// The specified column widths are set from column group and the first row for the fixed
/// table layout calculation.
/// The maximum min/pref widths of each column are set from the rows for the automatic
/// table layout calculation.
fn bubble_widths(&mut self, _: &mut LayoutContext) {
let mut min_width = Au(0);
let mut pref_width = Au(0);
let mut did_first_row = false;
let mut num_floats = 0;
/* find max width from child block contexts */
for kid in self.block_flow.base.child_iter() { for kid in self.block_flow.base.child_iter() {
assert!(kid.is_proper_table_child()); assert!(kid.is_proper_table_child());
if kid.is_table_colgroup() { if kid.is_table_colgroup() {
self.col_widths.push_all(kid.as_table_colgroup().widths); self.col_widths.push_all(kid.as_table_colgroup().widths);
self.col_min_widths = self.col_widths.clone();
self.col_pref_widths = self.col_widths.clone();
} else if kid.is_table_rowgroup() || kid.is_table_row() { } else if kid.is_table_rowgroup() || kid.is_table_row() {
// read column widths from table-row-group/table-row, and assign // read column widths from table-row-group/table-row, and assign
// width=0 for the columns not defined in column-group // width=0 for the columns not defined in column-group
// FIXME: need to read widths from either table-header-group OR // FIXME: need to read widths from either table-header-group OR
// first table-row // first table-row
let kid_col_widths = if kid.is_table_rowgroup() {
&kid.as_table_rowgroup().col_widths
} else {
&kid.as_table_row().col_widths
};
match self.table_layout { match self.table_layout {
FixedLayout if !did_first_row => { FixedLayout => {
did_first_row = true; let kid_col_widths = kid.col_widths();
let mut child_widths = kid_col_widths.iter(); if !did_first_row {
for col_width in self.col_widths.mut_iter() { did_first_row = true;
match child_widths.next() { let mut child_widths = kid_col_widths.iter();
Some(child_width) => { for col_width in self.col_widths.mut_iter() {
if *col_width == Au::new(0) { match child_widths.next() {
*col_width = *child_width; Some(child_width) => {
} if *col_width == Au::new(0) {
}, *col_width = *child_width;
None => break }
},
None => break
}
} }
} }
let num_child_cols = kid_col_widths.len();
let num_cols = self.col_widths.len();
debug!("table until the previous row has {} column(s) and this row has {} column(s)",
num_cols, num_child_cols);
for i in range(num_cols, num_child_cols) {
self.col_widths.push( kid_col_widths[i] );
}
}, },
_ => {} AutoLayout => {
} min_width = TableFlow::update_col_widths(&mut self.col_min_widths, kid.col_min_widths());
let num_child_cols = kid_col_widths.len(); pref_width = TableFlow::update_col_widths(&mut self.col_pref_widths, kid.col_pref_widths());
let num_cols = self.col_widths.len();
debug!("colgroup has {} column(s) and child has {} column(s)", num_cols, num_child_cols); // update the number of column widths from table-rows.
for i in range(num_cols, num_child_cols) { let num_cols = self.col_min_widths.len();
self.col_widths.push( kid_col_widths[i] ); let num_child_cols = kid.col_min_widths().len();
debug!("table until the previous row has {} column(s) and this row has {} column(s)",
num_cols, num_child_cols);
for i in range(num_cols, num_child_cols) {
self.col_widths.push(Au::new(0));
let new_kid_min = kid.col_min_widths()[i];
self.col_min_widths.push( new_kid_min );
let new_kid_pref = kid.col_pref_widths()[i];
self.col_pref_widths.push( new_kid_pref );
min_width = min_width + new_kid_min;
pref_width = pref_width + new_kid_pref;
}
}
} }
} }
let child_base = flow::mut_base(kid);
num_floats = num_floats + child_base.num_floats;
} }
self.block_flow.bubble_widths(ctx); for box_ in self.block_flow.box_.iter() {
box_.compute_borders(box_.style());
}
self.block_flow.base.num_floats = num_floats;
self.block_flow.base.intrinsic_widths.minimum_width = min_width;
self.block_flow.base.intrinsic_widths.preferred_width = geometry::max(min_width, pref_width);
} }
/// Recursively (top-down) determines the actual width of child contexts and boxes. When called /// Recursively (top-down) determines the actual width of child contexts and boxes. When called
@ -242,20 +279,25 @@ impl Flow for TableFlow {
content_width = box_.border_box.get().size.width - padding_and_borders; content_width = box_.border_box.get().size.width - padding_and_borders;
} }
// In fixed table layout, we distribute extra space among the unspecified columns if there are match self.table_layout {
// any, or among all the columns if all are specified. FixedLayout => {
if (total_column_width < content_width) && (num_unspecified_widths == 0) { // In fixed table layout, we distribute extra space among the unspecified columns if there are
let ratio = content_width.to_f64().unwrap() / total_column_width.to_f64().unwrap(); // any, or among all the columns if all are specified.
for col_width in self.col_widths.mut_iter() { if (total_column_width < content_width) && (num_unspecified_widths == 0) {
*col_width = (*col_width).scale_by(ratio); let ratio = content_width.to_f64().unwrap() / total_column_width.to_f64().unwrap();
} for col_width in self.col_widths.mut_iter() {
} else if num_unspecified_widths != 0 { *col_width = (*col_width).scale_by(ratio);
let extra_column_width = (content_width - total_column_width) / Au::new(num_unspecified_widths); }
for col_width in self.col_widths.mut_iter() { } else if num_unspecified_widths != 0 {
if *col_width == Au(0) { let extra_column_width = (content_width - total_column_width) / Au::new(num_unspecified_widths);
*col_width = extra_column_width; for col_width in self.col_widths.mut_iter() {
if *col_width == Au(0) {
*col_width = extra_column_width;
}
}
} }
} }
_ => {}
} }
self.block_flow.propagate_assigned_width_to_children(left_content_edge, content_width, Some(self.col_widths.clone())); self.block_flow.propagate_assigned_width_to_children(left_content_edge, content_width, Some(self.col_widths.clone()));
@ -275,20 +317,6 @@ impl Flow for TableFlow {
self.assign_height_table_base(ctx, false); self.assign_height_table_base(ctx, false);
} }
// CSS Section 8.3.1 - Collapsing Margins
// Since `margin` is not used on table box, `collapsing` and `collapsible` are set to 0
fn collapse_margins(&mut self,
_: bool,
_: &mut bool,
_: &mut Au,
_: &mut Au,
collapsing: &mut Au,
collapsible: &mut Au) {
// `margin` is not used on table box.
*collapsing = Au::new(0);
*collapsible = Au::new(0);
}
fn debug_str(&self) -> ~str { fn debug_str(&self) -> ~str {
let txt = ~"TableFlow: "; let txt = ~"TableFlow: ";
txt.append(match self.block_flow.box_ { txt.append(match self.block_flow.box_ {

View file

@ -7,14 +7,11 @@
use layout::block::BlockFlow; use layout::block::BlockFlow;
use layout::construct::FlowConstructor; use layout::construct::FlowConstructor;
use layout::context::LayoutContext; use layout::context::LayoutContext;
use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData}; use layout::display_list_builder::{DisplayListBuilder, DisplayListBuildingInfo};
use layout::flow::{TableCaptionFlowClass, FlowClass, Flow}; use layout::flow::{TableCaptionFlowClass, FlowClass, Flow};
use layout::wrapper::ThreadSafeLayoutNode; use layout::wrapper::ThreadSafeLayoutNode;
use std::cell::RefCell; use gfx::display_list::StackingContext;
use geom::{Point2D, Rect, Size2D};
use gfx::display_list::DisplayListCollection;
use servo_util::geometry::Au;
/// A table formatting context. /// A table formatting context.
pub struct TableCaptionFlow { pub struct TableCaptionFlow {
@ -34,19 +31,12 @@ impl TableCaptionFlow {
self.block_flow.teardown(); self.block_flow.teardown();
} }
pub fn build_display_list_table_caption<E:ExtraDisplayListData>( pub fn build_display_list_table_caption(&mut self,
&mut self, stacking_context: &mut StackingContext,
builder: &DisplayListBuilder, builder: &mut DisplayListBuilder,
container_block_size: &Size2D<Au>, info: &DisplayListBuildingInfo) {
absolute_cb_abs_position: Point2D<Au>,
dirty: &Rect<Au>,
index: uint,
lists: &RefCell<DisplayListCollection<E>>)
-> uint {
debug!("build_display_list_table_caption: same process as block flow"); debug!("build_display_list_table_caption: same process as block flow");
self.block_flow.build_display_list_block(builder, container_block_size, self.block_flow.build_display_list_block(stacking_context, builder, info)
absolute_cb_abs_position,
dirty, index, lists)
} }
} }
@ -86,13 +76,6 @@ impl Flow for TableCaptionFlow {
self.block_flow.assign_height(ctx); self.block_flow.assign_height(ctx);
} }
/// table-caption has margins but is not collapsed with a sibling(table)
/// or its parents(table-wrapper).
/// Therefore, margins to be collapsed do not exist.
fn collapse_margins(&mut self, _: bool, _: &mut bool, _: &mut Au,
_: &mut Au, _: &mut Au, _: &mut Au) {
}
fn debug_str(&self) -> ~str { fn debug_str(&self) -> ~str {
let txt = ~"TableCaptionFlow: "; let txt = ~"TableCaptionFlow: ";
txt.append(match self.block_flow.box_ { txt.append(match self.block_flow.box_ {

View file

@ -5,16 +5,15 @@
//! CSS table formatting contexts. //! CSS table formatting contexts.
use layout::box_::Box; use layout::box_::Box;
use layout::block::{BlockFlow, WidthAndMarginsComputer}; use layout::block::{BlockFlow, MarginsMayNotCollapse, WidthAndMarginsComputer};
use layout::context::LayoutContext; use layout::context::LayoutContext;
use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData}; use layout::display_list_builder::{DisplayListBuilder, DisplayListBuildingInfo};
use layout::flow::{TableCellFlowClass, FlowClass, Flow}; use layout::flow::{TableCellFlowClass, FlowClass, Flow};
use layout::model::{MaybeAuto};
use layout::table::InternalTable; use layout::table::InternalTable;
use layout::wrapper::ThreadSafeLayoutNode; use layout::wrapper::ThreadSafeLayoutNode;
use std::cell::RefCell; use gfx::display_list::StackingContext;
use geom::{Point2D, Rect, Size2D};
use gfx::display_list::{DisplayListCollection};
use servo_util::geometry::Au; use servo_util::geometry::Au;
/// A table formatting context. /// A table formatting context.
@ -42,62 +41,23 @@ impl TableCellFlow {
/// Assign height for table-cell flow. /// Assign height for table-cell flow.
/// ///
/// TODO(#2015, pcwalton): This doesn't handle floats right.
///
/// inline(always) because this is only ever called by in-order or non-in-order top-level /// inline(always) because this is only ever called by in-order or non-in-order top-level
/// methods /// methods
#[inline(always)] #[inline(always)]
fn assign_height_table_cell_base(&mut self, ctx: &mut LayoutContext, inorder: bool) { fn assign_height_table_cell_base(&mut self,
let (_, mut top_offset, bottom_offset, left_offset) = self.block_flow layout_context: &mut LayoutContext,
.initialize_offsets(true); inorder: bool) {
self.block_flow.assign_height_block_base(layout_context, inorder, MarginsMayNotCollapse)
self.block_flow.handle_children_floats_if_necessary(ctx, inorder,
left_offset, top_offset);
let mut cur_y = top_offset;
// Since table cell does not have `margin`, the first child's top margin and
// the last child's bottom margin do not collapse.
self.block_flow.compute_margin_collapse(&mut cur_y,
&mut top_offset,
&mut Au(0),
&mut Au(0),
false,
false);
// CSS 2.1 § 17.5.3. Table cell box height is the minimum height required by the content.
let height = cur_y - top_offset;
// TODO(june0cho): vertical-align of table-cell should be calculated.
let mut noncontent_height = Au::new(0);
for box_ in self.block_flow.box_.iter() {
let mut position = box_.border_box.get();
// noncontent_height = border_top/bottom + padding_top/bottom of box
noncontent_height = box_.noncontent_height();
position.origin.y = Au(0);
position.size.height = height + noncontent_height;
box_.border_box.set(position);
}
self.block_flow.base.position.size.height = height + noncontent_height;
self.block_flow.set_floats_out_if_inorder(inorder, height, cur_y, top_offset,
bottom_offset, left_offset);
self.block_flow.assign_height_absolute_flows(ctx);
} }
pub fn build_display_list_table_cell<E:ExtraDisplayListData>( pub fn build_display_list_table_cell(&mut self,
&mut self, stacking_context: &mut StackingContext,
builder: &DisplayListBuilder, builder: &mut DisplayListBuilder,
container_block_size: &Size2D<Au>, info: &DisplayListBuildingInfo) {
absolute_cb_abs_position: Point2D<Au>, debug!("build_display_list_table: same process as block flow");
dirty: &Rect<Au>, self.block_flow.build_display_list_block(stacking_context, builder, info)
index: uint,
lists: &RefCell<DisplayListCollection<E>>)
-> uint {
debug!("build_display_list_table_cell: same process as block flow");
self.block_flow.build_display_list_block(builder, container_block_size,
absolute_cb_abs_position,
dirty, index, lists)
} }
} }
@ -117,6 +77,18 @@ impl Flow for TableCellFlow {
/// Minimum/preferred widths set by this function are used in automatic table layout calculation. /// Minimum/preferred widths set by this function are used in automatic table layout calculation.
fn bubble_widths(&mut self, ctx: &mut LayoutContext) { fn bubble_widths(&mut self, ctx: &mut LayoutContext) {
self.block_flow.bubble_widths(ctx); self.block_flow.bubble_widths(ctx);
for box_ in self.block_flow.box_.iter() {
let specified_width = MaybeAuto::from_style(box_.style().Box.get().width,
Au::new(0)).specified_or_zero();
if self.block_flow.base.intrinsic_widths.minimum_width < specified_width {
self.block_flow.base.intrinsic_widths.minimum_width = specified_width;
}
if self.block_flow.base.intrinsic_widths.preferred_width <
self.block_flow.base.intrinsic_widths.minimum_width {
self.block_flow.base.intrinsic_widths.preferred_width =
self.block_flow.base.intrinsic_widths.minimum_width;
}
}
} }
/// Recursively (top-down) determines the actual width of child contexts and boxes. When called /// Recursively (top-down) determines the actual width of child contexts and boxes. When called
@ -156,12 +128,6 @@ impl Flow for TableCellFlow {
self.assign_height_table_cell_base(ctx, false); self.assign_height_table_cell_base(ctx, false);
} }
/// TableCellBox and their parents(TableRowBox) do not have margins.
/// Therefore, margins to be collapsed do not exist.
fn collapse_margins(&mut self, _: bool, _: &mut bool, _: &mut Au,
_: &mut Au, _: &mut Au, _: &mut Au) {
}
fn debug_str(&self) -> ~str { fn debug_str(&self) -> ~str {
let txt = ~"TableCellFlow: "; let txt = ~"TableCellFlow: ";
txt.append(match self.block_flow.box_ { txt.append(match self.block_flow.box_ {

View file

@ -82,12 +82,6 @@ impl Flow for TableColGroupFlow {
fn assign_height(&mut self, _ctx: &mut LayoutContext) { fn assign_height(&mut self, _ctx: &mut LayoutContext) {
} }
/// TableColumnBox and their parents(TableBox) do not have margins.
/// Therefore, margins to be collapsed do not exist.
fn collapse_margins(&mut self, _: bool, _: &mut bool, _: &mut Au,
_: &mut Au, _: &mut Au, _: &mut Au) {
}
fn debug_str(&self) -> ~str { fn debug_str(&self) -> ~str {
let txt = ~"TableColGroupFlow: "; let txt = ~"TableColGroupFlow: ";
txt.append(match self.box_ { txt.append(match self.box_ {

View file

@ -9,16 +9,14 @@ use layout::block::BlockFlow;
use layout::block::WidthAndMarginsComputer; use layout::block::WidthAndMarginsComputer;
use layout::construct::FlowConstructor; use layout::construct::FlowConstructor;
use layout::context::LayoutContext; use layout::context::LayoutContext;
use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData}; use layout::display_list_builder::{DisplayListBuilder, DisplayListBuildingInfo};
use layout::flow::{TableRowFlowClass, FlowClass, Flow, ImmutableFlowUtils}; use layout::flow::{TableRowFlowClass, FlowClass, Flow, ImmutableFlowUtils};
use layout::flow; use layout::flow;
use layout::table::InternalTable; use layout::table::InternalTable;
use layout::model::{MaybeAuto, Specified, Auto}; use layout::model::{MaybeAuto, Specified, Auto};
use layout::wrapper::ThreadSafeLayoutNode; use layout::wrapper::ThreadSafeLayoutNode;
use std::cell::RefCell; use gfx::display_list::StackingContext;
use geom::{Point2D, Rect, Size2D};
use gfx::display_list::DisplayListCollection;
use servo_util::geometry::Au; use servo_util::geometry::Au;
use servo_util::geometry; use servo_util::geometry;
@ -28,6 +26,12 @@ pub struct TableRowFlow {
/// Column widths. /// Column widths.
col_widths: ~[Au], col_widths: ~[Au],
/// Column min widths.
col_min_widths: ~[Au],
/// Column pref widths.
col_pref_widths: ~[Au],
} }
impl TableRowFlow { impl TableRowFlow {
@ -37,6 +41,8 @@ impl TableRowFlow {
TableRowFlow { TableRowFlow {
block_flow: BlockFlow::from_node_and_box(node, box_), block_flow: BlockFlow::from_node_and_box(node, box_),
col_widths: ~[], col_widths: ~[],
col_min_widths: ~[],
col_pref_widths: ~[],
} }
} }
@ -46,12 +52,16 @@ impl TableRowFlow {
TableRowFlow { TableRowFlow {
block_flow: BlockFlow::from_node(constructor, node), block_flow: BlockFlow::from_node(constructor, node),
col_widths: ~[], col_widths: ~[],
col_min_widths: ~[],
col_pref_widths: ~[],
} }
} }
pub fn teardown(&mut self) { pub fn teardown(&mut self) {
self.block_flow.teardown(); self.block_flow.teardown();
self.col_widths = ~[]; self.col_widths = ~[];
self.col_min_widths = ~[];
self.col_pref_widths = ~[];
} }
pub fn box_<'a>(&'a mut self) -> &'a Option<Box>{ pub fn box_<'a>(&'a mut self) -> &'a Option<Box>{
@ -66,19 +76,23 @@ impl TableRowFlow {
/// Assign height for table-row flow. /// Assign height for table-row flow.
/// ///
/// TODO(pcwalton): This doesn't handle floats and positioned elements right.
///
/// inline(always) because this is only ever called by in-order or non-in-order top-level /// inline(always) because this is only ever called by in-order or non-in-order top-level
/// methods /// methods
#[inline(always)] #[inline(always)]
fn assign_height_table_row_base(&mut self, ctx: &mut LayoutContext, inorder: bool) { fn assign_height_table_row_base(&mut self, layout_context: &mut LayoutContext, inorder: bool) {
let (top_offset, bottom_offset, left_offset) = self.initialize_offsets(); let (top_offset, _, _) = self.initialize_offsets();
self.block_flow.handle_children_floats_if_necessary(ctx, inorder,
left_offset, top_offset);
let mut cur_y = top_offset; let mut cur_y = top_offset;
// Per CSS 2.1 § 17.5.3, find max_y = max( computed `height`, minimum height of all cells ) // Per CSS 2.1 § 17.5.3, find max_y = max( computed `height`, minimum height of all cells )
let mut max_y = Au::new(0); let mut max_y = Au::new(0);
for kid in self.block_flow.base.child_iter() { for kid in self.block_flow.base.child_iter() {
if inorder {
kid.assign_height_inorder(layout_context)
}
for child_box in kid.as_table_cell().box_().iter() { for child_box in kid.as_table_cell().box_().iter() {
// TODO: Percentage height // TODO: Percentage height
let child_specified_height = MaybeAuto::from_style(child_box.style().Box.get().height, let child_specified_height = MaybeAuto::from_style(child_box.style().Box.get().height,
@ -101,6 +115,8 @@ impl TableRowFlow {
cur_y = cur_y + height; cur_y = cur_y + height;
// Assign the height of own box // Assign the height of own box
//
// FIXME(pcwalton): Take `cur_y` into account.
for box_ in self.block_flow.box_.iter() { for box_ in self.block_flow.box_.iter() {
let mut position = box_.border_box.get(); let mut position = box_.border_box.get();
position.size.height = height; position.size.height = height;
@ -118,24 +134,14 @@ impl TableRowFlow {
let child_node = flow::mut_base(kid); let child_node = flow::mut_base(kid);
child_node.position.size.height = height; child_node.position.size.height = height;
} }
self.block_flow.set_floats_out_if_inorder(inorder, height, cur_y,
top_offset, bottom_offset, left_offset);
} }
pub fn build_display_list_table_row<E:ExtraDisplayListData>( pub fn build_display_list_table_row(&mut self,
&mut self, stacking_context: &mut StackingContext,
builder: &DisplayListBuilder, builder: &mut DisplayListBuilder,
container_block_size: &Size2D<Au>, info: &DisplayListBuildingInfo) {
absolute_cb_abs_position: Point2D<Au>,
dirty: &Rect<Au>,
index: uint,
lists: &RefCell<DisplayListCollection<E>>)
-> uint {
debug!("build_display_list_table_row: same process as block flow"); debug!("build_display_list_table_row: same process as block flow");
self.block_flow.build_display_list_block(builder, container_block_size, self.block_flow.build_display_list_block(stacking_context, builder, info)
absolute_cb_abs_position,
dirty, index, lists)
} }
} }
@ -152,27 +158,51 @@ impl Flow for TableRowFlow {
&mut self.block_flow &mut self.block_flow
} }
fn col_widths<'a>(&'a mut self) -> &'a mut ~[Au] {
&mut self.col_widths
}
fn col_min_widths<'a>(&'a self) -> &'a ~[Au] {
&self.col_min_widths
}
fn col_pref_widths<'a>(&'a self) -> &'a ~[Au] {
&self.col_pref_widths
}
/// Recursively (bottom-up) determines the context's preferred and minimum widths. When called /// Recursively (bottom-up) determines the context's preferred and minimum widths. When called
/// on this context, all child contexts have had their min/pref widths set. This function must /// on this context, all child contexts have had their min/pref widths set. This function must
/// decide min/pref widths based on child context widths and dimensions of any boxes it is /// decide min/pref widths based on child context widths and dimensions of any boxes it is
/// responsible for flowing. /// responsible for flowing.
/// Min/pref widths set by this function are used in automatic table layout calculation. /// Min/pref widths set by this function are used in automatic table layout calculation.
/// Also, this function collects the specified column widths of children cells. Those are used /// The specified column widths of children cells are used in fixed table layout calculation.
/// in fixed table layout calculation fn bubble_widths(&mut self, _: &mut LayoutContext) {
fn bubble_widths(&mut self, ctx: &mut LayoutContext) { let mut min_width = Au(0);
let mut pref_width = Au(0);
let mut num_floats = 0;
/* find the specified widths from child table-cell contexts */ /* find the specified widths from child table-cell contexts */
for kid in self.block_flow.base.child_iter() { for kid in self.block_flow.base.child_iter() {
assert!(kid.is_table_cell()); assert!(kid.is_table_cell());
// collect the specified column widths of cells. These are used in fixed table layout calculation.
for child_box in kid.as_table_cell().box_().iter() { for child_box in kid.as_table_cell().box_().iter() {
let child_specified_width = MaybeAuto::from_style(child_box.style().Box.get().width, let child_specified_width = MaybeAuto::from_style(child_box.style().Box.get().width,
Au::new(0)).specified_or_zero(); Au::new(0)).specified_or_zero();
self.col_widths.push(child_specified_width); self.col_widths.push(child_specified_width);
} }
}
// TODO: calculate min_width & pref_width for automatic table layout calculation // collect min_width & pref_width of children cells for automatic table layout calculation.
self.block_flow.bubble_widths(ctx); let child_base = flow::mut_base(kid);
self.col_min_widths.push(child_base.intrinsic_widths.minimum_width);
self.col_pref_widths.push(child_base.intrinsic_widths.preferred_width);
min_width = min_width + child_base.intrinsic_widths.minimum_width;
pref_width = pref_width + child_base.intrinsic_widths.preferred_width;
num_floats = num_floats + child_base.num_floats;
}
self.block_flow.base.num_floats = num_floats;
self.block_flow.base.intrinsic_widths.minimum_width = min_width;
self.block_flow.base.intrinsic_widths.preferred_width = geometry::max(min_width,
pref_width);
} }
/// Recursively (top-down) determines the actual width of child contexts and boxes. When called /// Recursively (top-down) determines the actual width of child contexts and boxes. When called
@ -205,12 +235,6 @@ impl Flow for TableRowFlow {
self.assign_height_table_row_base(ctx, false); self.assign_height_table_row_base(ctx, false);
} }
/// TableRowBox and their parents(TableBox) do not have margins.
/// Therefore, margins to be collapsed do not exist.
fn collapse_margins(&mut self, _: bool, _: &mut bool, _: &mut Au,
_: &mut Au, _: &mut Au, _: &mut Au) {
}
fn debug_str(&self) -> ~str { fn debug_str(&self) -> ~str {
let txt = ~"TableRowFlow: "; let txt = ~"TableRowFlow: ";
txt.append(match self.block_flow.box_ { txt.append(match self.block_flow.box_ {

View file

@ -9,16 +9,15 @@ use layout::block::BlockFlow;
use layout::block::WidthAndMarginsComputer; use layout::block::WidthAndMarginsComputer;
use layout::construct::FlowConstructor; use layout::construct::FlowConstructor;
use layout::context::LayoutContext; use layout::context::LayoutContext;
use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData}; use layout::display_list_builder::{DisplayListBuilder, DisplayListBuildingInfo};
use layout::flow::{TableRowGroupFlowClass, FlowClass, Flow, ImmutableFlowUtils}; use layout::flow::{TableRowGroupFlowClass, FlowClass, Flow, ImmutableFlowUtils};
use layout::flow; use layout::flow;
use layout::table::InternalTable; use layout::table::{InternalTable, TableFlow};
use layout::wrapper::ThreadSafeLayoutNode; use layout::wrapper::ThreadSafeLayoutNode;
use std::cell::RefCell; use gfx::display_list::StackingContext;
use geom::{Point2D, Rect, Size2D};
use gfx::display_list::DisplayListCollection;
use servo_util::geometry::Au; use servo_util::geometry::Au;
use servo_util::geometry;
/// A table formatting context. /// A table formatting context.
pub struct TableRowGroupFlow { pub struct TableRowGroupFlow {
@ -26,6 +25,12 @@ pub struct TableRowGroupFlow {
/// Column widths /// Column widths
col_widths: ~[Au], col_widths: ~[Au],
/// Column min widths.
col_min_widths: ~[Au],
/// Column pref widths.
col_pref_widths: ~[Au],
} }
impl TableRowGroupFlow { impl TableRowGroupFlow {
@ -35,6 +40,8 @@ impl TableRowGroupFlow {
TableRowGroupFlow { TableRowGroupFlow {
block_flow: BlockFlow::from_node_and_box(node, box_), block_flow: BlockFlow::from_node_and_box(node, box_),
col_widths: ~[], col_widths: ~[],
col_min_widths: ~[],
col_pref_widths: ~[],
} }
} }
@ -44,12 +51,16 @@ impl TableRowGroupFlow {
TableRowGroupFlow { TableRowGroupFlow {
block_flow: BlockFlow::from_node(constructor, node), block_flow: BlockFlow::from_node(constructor, node),
col_widths: ~[], col_widths: ~[],
col_min_widths: ~[],
col_pref_widths: ~[],
} }
} }
pub fn teardown(&mut self) { pub fn teardown(&mut self) {
self.block_flow.teardown(); self.block_flow.teardown();
self.col_widths = ~[]; self.col_widths = ~[];
self.col_min_widths = ~[];
self.col_pref_widths = ~[];
} }
pub fn box_<'a>(&'a mut self) -> &'a Option<Box>{ pub fn box_<'a>(&'a mut self) -> &'a Option<Box>{
@ -64,14 +75,14 @@ impl TableRowGroupFlow {
/// Assign height for table-rowgroup flow. /// Assign height for table-rowgroup flow.
/// ///
/// FIXME(pcwalton): This doesn't handle floats right.
///
/// inline(always) because this is only ever called by in-order or non-in-order top-level /// inline(always) because this is only ever called by in-order or non-in-order top-level
/// methods /// methods
#[inline(always)] #[inline(always)]
fn assign_height_table_rowgroup_base(&mut self, ctx: &mut LayoutContext, inorder: bool) { fn assign_height_table_rowgroup_base(&mut self, _: &mut LayoutContext, _: bool) {
let (top_offset, bottom_offset, left_offset) = self.initialize_offsets(); let (top_offset, _, _) = self.initialize_offsets();
self.block_flow.handle_children_floats_if_necessary(ctx, inorder,
left_offset, top_offset);
let mut cur_y = top_offset; let mut cur_y = top_offset;
for kid in self.block_flow.base.child_iter() { for kid in self.block_flow.base.child_iter() {
@ -88,24 +99,14 @@ impl TableRowGroupFlow {
box_.border_box.set(position); box_.border_box.set(position);
} }
self.block_flow.base.position.size.height = height; self.block_flow.base.position.size.height = height;
self.block_flow.set_floats_out_if_inorder(inorder, height, cur_y,
top_offset, bottom_offset, left_offset);
} }
pub fn build_display_list_table_rowgroup<E:ExtraDisplayListData>( pub fn build_display_list_table_rowgroup(&mut self,
&mut self, stacking_context: &mut StackingContext,
builder: &DisplayListBuilder, builder: &mut DisplayListBuilder,
container_block_size: &Size2D<Au>, info: &DisplayListBuildingInfo) {
absolute_cb_abs_position: Point2D<Au>,
dirty: &Rect<Au>,
index: uint,
lists: &RefCell<DisplayListCollection<E>>)
-> uint {
debug!("build_display_list_table_rowgroup: same process as block flow"); debug!("build_display_list_table_rowgroup: same process as block flow");
self.block_flow.build_display_list_block(builder, container_block_size, self.block_flow.build_display_list_block(stacking_context, builder, info)
absolute_cb_abs_position,
dirty, index, lists)
} }
} }
@ -122,6 +123,18 @@ impl Flow for TableRowGroupFlow {
&mut self.block_flow &mut self.block_flow
} }
fn col_widths<'a>(&'a mut self) -> &'a mut ~[Au] {
&mut self.col_widths
}
fn col_min_widths<'a>(&'a self) -> &'a ~[Au] {
&self.col_min_widths
}
fn col_pref_widths<'a>(&'a self) -> &'a ~[Au] {
&self.col_pref_widths
}
/// Recursively (bottom-up) determines the context's preferred and minimum widths. When called /// Recursively (bottom-up) determines the context's preferred and minimum widths. When called
/// on this context, all child contexts have had their min/pref widths set. This function must /// on this context, all child contexts have had their min/pref widths set. This function must
/// decide min/pref widths based on child context widths and dimensions of any boxes it is /// decide min/pref widths based on child context widths and dimensions of any boxes it is
@ -129,24 +142,48 @@ impl Flow for TableRowGroupFlow {
/// Min/pref widths set by this function are used in automatic table layout calculation. /// Min/pref widths set by this function are used in automatic table layout calculation.
/// Also, this function finds the specified column widths from the first row. /// Also, this function finds the specified column widths from the first row.
/// Those are used in fixed table layout calculation /// Those are used in fixed table layout calculation
fn bubble_widths(&mut self, ctx: &mut LayoutContext) { fn bubble_widths(&mut self, _: &mut LayoutContext) {
/* find the specified column widths from the first table-row. let mut min_width = Au(0);
update the number of column widths from other table-rows. */ let mut pref_width = Au(0);
let mut num_floats = 0;
for kid in self.block_flow.base.child_iter() { for kid in self.block_flow.base.child_iter() {
assert!(kid.is_table_row()); assert!(kid.is_table_row());
if self.col_widths.is_empty() {
self.col_widths = kid.as_table_row().col_widths.clone(); // calculate min_width & pref_width for automatic table layout calculation
// 'self.col_min_widths' collects the maximum value of cells' min-widths for each column.
// 'self.col_pref_widths' collects the maximum value of cells' pref-widths for each column.
if self.col_widths.is_empty() { // First Row
assert!(self.col_min_widths.is_empty() && self.col_pref_widths.is_empty());
// 'self.col_widths' collects the specified column widths from the first table-row for fixed table layout calculation.
self.col_widths = kid.col_widths().clone();
self.col_min_widths = kid.col_min_widths().clone();
self.col_pref_widths = kid.col_pref_widths().clone();
} else { } else {
min_width = TableFlow::update_col_widths(&mut self.col_min_widths, kid.col_min_widths());
pref_width = TableFlow::update_col_widths(&mut self.col_pref_widths, kid.col_pref_widths());
// update the number of column widths from table-rows.
let num_cols = self.col_widths.len(); let num_cols = self.col_widths.len();
let num_child_cols = kid.as_table_row().col_widths.len(); let num_child_cols = kid.col_min_widths().len();
for _ in range(num_cols, num_child_cols) { for i in range(num_cols, num_child_cols) {
self.col_widths.push(Au::new(0)); self.col_widths.push(Au::new(0));
let new_kid_min = kid.col_min_widths()[i];
self.col_min_widths.push(kid.col_min_widths()[i]);
let new_kid_pref = kid.col_pref_widths()[i];
self.col_pref_widths.push(kid.col_pref_widths()[i]);
min_width = min_width + new_kid_min;
pref_width = pref_width + new_kid_pref;
} }
} }
let child_base = flow::mut_base(kid);
num_floats = num_floats + child_base.num_floats;
} }
// TODO: calculate min_width & pref_width for automatic table layout calculation self.block_flow.base.num_floats = num_floats;
self.block_flow.bubble_widths(ctx); self.block_flow.base.intrinsic_widths.minimum_width = min_width;
self.block_flow.base.intrinsic_widths.preferred_width = geometry::max(min_width,
pref_width);
} }
/// Recursively (top-down) determines the actual width of child contexts and boxes. When called /// Recursively (top-down) determines the actual width of child contexts and boxes. When called
@ -180,12 +217,6 @@ impl Flow for TableRowGroupFlow {
self.assign_height_table_rowgroup_base(ctx, false); self.assign_height_table_rowgroup_base(ctx, false);
} }
/// TableRowBox and their parents(TableBox) do not have margins.
/// Therefore, margins to be collapsed do not exist.
fn collapse_margins(&mut self, _: bool, _: &mut bool, _: &mut Au,
_: &mut Au, _: &mut Au, _: &mut Au) {
}
fn debug_str(&self) -> ~str { fn debug_str(&self) -> ~str {
let txt = ~"TableRowGroupFlow: "; let txt = ~"TableRowGroupFlow: ";
txt.append(match self.block_flow.box_ { txt.append(match self.block_flow.box_ {

View file

@ -5,23 +5,20 @@
//! CSS table formatting contexts. //! CSS table formatting contexts.
use layout::box_::Box; use layout::box_::Box;
use layout::block::BlockFlow; use layout::block::{BlockFlow, MarginsMayNotCollapse, WidthAndMarginsComputer};
use layout::block::{WidthAndMarginsComputer, WidthConstraintInput, WidthConstraintSolution}; use layout::block::{WidthConstraintInput, WidthConstraintSolution};
use layout::construct::FlowConstructor; use layout::construct::FlowConstructor;
use layout::context::LayoutContext; use layout::context::LayoutContext;
use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData}; use layout::display_list_builder::{DisplayListBuilder, DisplayListBuildingInfo};
use layout::floats::{FloatKind}; use layout::floats::FloatKind;
use layout::flow::{TableWrapperFlowClass, FlowClass, Flow, ImmutableFlowUtils}; use layout::flow::{TableWrapperFlowClass, FlowClass, Flow, ImmutableFlowUtils};
use layout::flow; use layout::model::{Specified, Auto, specified};
use layout::model::{MaybeAuto, Specified, Auto, specified};
use layout::wrapper::ThreadSafeLayoutNode; use layout::wrapper::ThreadSafeLayoutNode;
use std::cell::RefCell; use gfx::display_list::StackingContext;
use style::computed_values::table_layout;
use geom::{Point2D, Rect, Size2D};
use gfx::display_list::DisplayListCollection;
use servo_util::geometry::Au; use servo_util::geometry::Au;
use servo_util::geometry; use servo_util::geometry;
use style::computed_values::table_layout;
pub enum TableLayout { pub enum TableLayout {
FixedLayout, FixedLayout,
@ -103,77 +100,22 @@ impl TableWrapperFlow {
/// Assign height for table-wrapper flow. /// Assign height for table-wrapper flow.
/// `Assign height` of table-wrapper flow follows a similar process to that of block flow. /// `Assign height` of table-wrapper flow follows a similar process to that of block flow.
/// However, table-wrapper flow doesn't consider collapsing margins for flow's children
/// and calculating padding/border.
/// ///
/// inline(always) because this is only ever called by in-order or non-in-order top-level /// inline(always) because this is only ever called by in-order or non-in-order top-level
/// methods /// methods
#[inline(always)] #[inline(always)]
fn assign_height_table_wrapper_base(&mut self, ctx: &mut LayoutContext, inorder: bool) { fn assign_height_table_wrapper_base(&mut self,
layout_context: &mut LayoutContext,
// Note: Ignoring clearance for absolute flows as of now. inorder: bool) {
let ignore_clear = self.is_absolutely_positioned(); self.block_flow.assign_height_block_base(layout_context, inorder, MarginsMayNotCollapse);
let (clearance, top_offset, bottom_offset, left_offset) = self.block_flow.initialize_offsets(ignore_clear);
self.block_flow.handle_children_floats_if_necessary(ctx, inorder,
left_offset, top_offset);
// Table wrapper flow has margin but is not collapsed with kids(table caption and table).
let (margin_top, margin_bottom, _, _) = self.block_flow.precompute_margin();
let mut cur_y = top_offset;
for kid in self.block_flow.base.child_iter() {
let child_node = flow::mut_base(kid);
child_node.position.origin.y = cur_y;
cur_y = cur_y + child_node.position.size.height;
}
// top_offset: top margin-edge of the topmost child.
// hence, height = content height
let mut height = cur_y - top_offset;
// For an absolutely positioned element, store the content height and stop the function.
if self.block_flow.store_content_height_if_absolutely_positioned(height) {
return;
}
for box_ in self.block_flow.box_.iter() {
let style = box_.style();
// At this point, `height` is the height of the containing block, so passing `height`
// as the second argument here effectively makes percentages relative to the containing
// block per CSS 2.1 § 10.5.
height = match MaybeAuto::from_style(style.Box.get().height, height) {
Auto => height,
Specified(value) => geometry::max(value, height)
};
}
self.block_flow.compute_height_position(&mut height,
Au(0),
margin_top,
margin_bottom,
clearance);
self.block_flow.set_floats_out_if_inorder(inorder, height, cur_y,
top_offset, bottom_offset, left_offset);
self.block_flow.assign_height_absolute_flows(ctx);
} }
pub fn build_display_list_table_wrapper<E:ExtraDisplayListData>( pub fn build_display_list_table_wrapper(&mut self,
&mut self, stacking_context: &mut StackingContext,
builder: &DisplayListBuilder, builder: &mut DisplayListBuilder,
container_block_size: &Size2D<Au>, info: &DisplayListBuildingInfo) {
absolute_cb_abs_position: Point2D<Au>,
dirty: &Rect<Au>,
index: uint,
lists: &RefCell<DisplayListCollection<E>>)
-> uint {
debug!("build_display_list_table_wrapper: same process as block flow"); debug!("build_display_list_table_wrapper: same process as block flow");
self.block_flow.build_display_list_block(builder, container_block_size, self.block_flow.build_display_list_block(stacking_context, builder, info);
absolute_cb_abs_position,
dirty, index, lists)
} }
} }
@ -197,7 +139,7 @@ impl Flow for TableWrapperFlow {
any boxes it is responsible for flowing. */ any boxes it is responsible for flowing. */
fn bubble_widths(&mut self, ctx: &mut LayoutContext) { fn bubble_widths(&mut self, ctx: &mut LayoutContext) {
/* find max width from child block contexts */ // get column widths info from table flow
for kid in self.block_flow.base.child_iter() { for kid in self.block_flow.base.child_iter() {
assert!(kid.is_table_caption() || kid.is_table()); assert!(kid.is_table_caption() || kid.is_table());
@ -227,8 +169,6 @@ impl Flow for TableWrapperFlow {
let mut left_content_edge = Au::new(0); let mut left_content_edge = Au::new(0);
let mut content_width = containing_block_width; let mut content_width = containing_block_width;
self.block_flow.set_containing_width_if_float(containing_block_width);
let width_computer = TableWrapper; let width_computer = TableWrapper;
width_computer.compute_used_width_table_wrapper(self, ctx, containing_block_width); width_computer.compute_used_width_table_wrapper(self, ctx, containing_block_width);
@ -243,7 +183,12 @@ impl Flow for TableWrapperFlow {
_ => {} _ => {}
} }
self.block_flow.propagate_assigned_width_to_children(left_content_edge, content_width, None); // In case of fixed layout, column widths are calculated in table flow.
let assigned_col_widths = match self.table_layout {
FixedLayout => None,
AutoLayout => Some(self.col_widths.clone())
};
self.block_flow.propagate_assigned_width_to_children(left_content_edge, content_width, assigned_col_widths);
} }
/// This is called on kid flows by a parent. /// This is called on kid flows by a parent.
@ -270,26 +215,6 @@ impl Flow for TableWrapperFlow {
} }
} }
// CSS Section 8.3.1 - Collapsing Margins
// `self`: the Flow whose margins we want to collapse.
// `collapsing`: value to be set by this function. This tells us how much
// of the top margin has collapsed with a previous margin.
// `collapsible`: Potential collapsible margin at the bottom of this flow's box.
fn collapse_margins(&mut self,
top_margin_collapsible: bool,
first_in_flow: &mut bool,
margin_top: &mut Au,
top_offset: &mut Au,
collapsing: &mut Au,
collapsible: &mut Au) {
self.block_flow.collapse_margins(top_margin_collapsible,
first_in_flow,
margin_top,
top_offset,
collapsing,
collapsible);
}
fn debug_str(&self) -> ~str { fn debug_str(&self) -> ~str {
let txt = if self.is_float() { let txt = if self.is_float() {
~"TableWrapperFlow(Float): " ~"TableWrapperFlow(Float): "
@ -327,10 +252,12 @@ impl TableWrapper {
let mut input = self.compute_width_constraint_inputs(&mut table_wrapper.block_flow, let mut input = self.compute_width_constraint_inputs(&mut table_wrapper.block_flow,
parent_flow_width, parent_flow_width,
ctx); ctx);
match table_wrapper.table_layout { let computed_width = match table_wrapper.table_layout {
FixedLayout => { FixedLayout => {
let fixed_cells_width = table_wrapper.col_widths.iter().fold(Au(0), let fixed_cells_width = table_wrapper.col_widths.iter().fold(Au(0),
|sum, width| sum.add(width)); |sum, width| sum.add(width));
let mut computed_width = input.computed_width.specified_or_zero();
for box_ in table_wrapper.block_flow.box_.iter() { for box_ in table_wrapper.block_flow.box_.iter() {
let style = box_.style(); let style = box_.style();
@ -344,15 +271,72 @@ impl TableWrapper {
let border_left = style.Border.get().border_left_width; let border_left = style.Border.get().border_left_width;
let border_right = style.Border.get().border_right_width; let border_right = style.Border.get().border_right_width;
let padding_and_borders = padding_left + padding_right + border_left + border_right; let padding_and_borders = padding_left + padding_right + border_left + border_right;
let mut computed_width = input.computed_width.specified_or_zero();
// Compare border-edge widths. Because fixed_cells_width indicates content-width, // Compare border-edge widths. Because fixed_cells_width indicates content-width,
// padding and border values are added to fixed_cells_width. // padding and border values are added to fixed_cells_width.
computed_width = geometry::max(fixed_cells_width + padding_and_borders, computed_width); computed_width = geometry::max(fixed_cells_width + padding_and_borders, computed_width);
input.computed_width = Specified(computed_width);
} }
computed_width
}, },
_ => {} AutoLayout => {
} // Automatic table layout is calculated according to CSS 2.1 § 17.5.2.2.
let mut cap_min = Au(0);
let mut cols_min = Au(0);
let mut cols_max = Au(0);
let mut col_min_widths = &~[];
let mut col_pref_widths = &~[];
for kid in table_wrapper.block_flow.base.child_iter() {
if kid.is_table_caption() {
cap_min = kid.as_block().base.intrinsic_widths.minimum_width;
} else {
assert!(kid.is_table());
cols_min = kid.as_block().base.intrinsic_widths.minimum_width;
cols_max = kid.as_block().base.intrinsic_widths.preferred_width;
col_min_widths = kid.col_min_widths();
col_pref_widths = kid.col_pref_widths();
}
}
// 'extra_width': difference between the calculated table width and minimum width
// required by all columns. It will be distributed over the columns.
let (width, extra_width) = match input.computed_width {
Auto => {
if input.available_width > geometry::max(cols_max, cap_min) {
if cols_max > cap_min {
table_wrapper.col_widths = col_pref_widths.clone();
(cols_max, Au(0))
} else {
(cap_min, cap_min - cols_min)
}
} else {
let max = if cols_min >= input.available_width && cols_min >= cap_min {
table_wrapper.col_widths = col_min_widths.clone();
cols_min
} else {
geometry::max(input.available_width, cap_min)
};
(max, max - cols_min)
}
},
Specified(width) => {
let max = if cols_min >= width && cols_min >= cap_min {
table_wrapper.col_widths = col_min_widths.clone();
cols_min
} else {
geometry::max(width, cap_min)
};
(max, max - cols_min)
}
};
// The extra width is distributed over the columns
if extra_width > Au(0) {
let cell_len = table_wrapper.col_widths.len() as f64;
table_wrapper.col_widths = col_min_widths.map(|width| {
width + extra_width.scale_by(1.0 / cell_len)
});
}
width
}
};
input.computed_width = Specified(computed_width);
input input
} }
} }

View file

@ -7,6 +7,7 @@ use layout::construct::{ConstructionResult, NoConstructionResult};
use layout::parallel::DomParallelInfo; use layout::parallel::DomParallelInfo;
use layout::wrapper::{LayoutNode, TLayoutNode, ThreadSafeLayoutNode}; use layout::wrapper::{LayoutNode, TLayoutNode, ThreadSafeLayoutNode};
use gfx::display_list::OpaqueNode;
use script::dom::bindings::js::JS; use script::dom::bindings::js::JS;
use script::dom::bindings::utils::Reflectable; use script::dom::bindings::utils::Reflectable;
use script::dom::node::Node; use script::dom::node::Node;
@ -146,6 +147,10 @@ pub struct PrivateLayoutData {
/// `ConstructionItem`. See comments in `construct.rs` for more details. /// `ConstructionItem`. See comments in `construct.rs` for more details.
flow_construction_result: ConstructionResult, flow_construction_result: ConstructionResult,
before_flow_construction_result: ConstructionResult,
after_flow_construction_result: ConstructionResult,
/// Information needed during parallel traversals. /// Information needed during parallel traversals.
parallel: DomParallelInfo, parallel: DomParallelInfo,
} }
@ -159,6 +164,8 @@ impl PrivateLayoutData {
after_style: None, after_style: None,
restyle_damage: None, restyle_damage: None,
flow_construction_result: NoConstructionResult, flow_construction_result: NoConstructionResult,
before_flow_construction_result: NoConstructionResult,
after_flow_construction_result: NoConstructionResult,
parallel: DomParallelInfo::new(), parallel: DomParallelInfo::new(),
} }
} }
@ -200,40 +207,45 @@ impl<'ln> LayoutDataAccess for LayoutNode<'ln> {
} }
} }
/// An opaque handle to a node. The only safe operation that can be performed on this node is to pub trait OpaqueNodeMethods {
/// 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 {
/// Converts a DOM node (layout view) to an `OpaqueNode`. /// Converts a DOM node (layout view) to an `OpaqueNode`.
pub fn from_layout_node(node: &LayoutNode) -> OpaqueNode { fn from_layout_node(node: &LayoutNode) -> Self;
/// Converts a thread-safe DOM node (layout view) to an `OpaqueNode`.
fn from_thread_safe_layout_node(node: &ThreadSafeLayoutNode) -> Self;
/// Converts a DOM node (script view) to an `OpaqueNode`.
fn from_script_node(node: TrustedNodeAddress) -> Self;
/// Converts a DOM node to an `OpaqueNode'.
fn from_jsmanaged(node: &JS<Node>) -> Self;
/// Converts this node to an `UntrustedNodeAddress`. An `UntrustedNodeAddress` is just the type
/// of node that script expects to receive in a hit test.
fn to_untrusted_node_address(&self) -> UntrustedNodeAddress;
}
impl OpaqueNodeMethods for OpaqueNode {
fn from_layout_node(node: &LayoutNode) -> OpaqueNode {
unsafe { unsafe {
OpaqueNode::from_jsmanaged(node.get_jsmanaged()) OpaqueNodeMethods::from_jsmanaged(node.get_jsmanaged())
} }
} }
/// Converts a thread-safe DOM node (layout view) to an `OpaqueNode`. fn from_thread_safe_layout_node(node: &ThreadSafeLayoutNode) -> OpaqueNode {
pub fn from_thread_safe_layout_node(node: &ThreadSafeLayoutNode) -> OpaqueNode {
unsafe { unsafe {
let abstract_node = node.get_jsmanaged(); let abstract_node = node.get_jsmanaged();
let ptr: uintptr_t = cast::transmute(abstract_node.reflector().get_jsobject()); let ptr: uintptr_t = abstract_node.reflector().get_jsobject() as uint;
OpaqueNode(ptr) OpaqueNode(ptr)
} }
} }
/// Converts a DOM node (script view) to an `OpaqueNode`. fn from_script_node(node: TrustedNodeAddress) -> OpaqueNode {
pub fn from_script_node(node: TrustedNodeAddress) -> OpaqueNode {
unsafe { unsafe {
OpaqueNode::from_jsmanaged(&JS::from_trusted_node_address(node)) OpaqueNodeMethods::from_jsmanaged(&JS::from_trusted_node_address(node))
} }
} }
/// Converts a DOM node to an `OpaqueNode'.
fn from_jsmanaged(node: &JS<Node>) -> OpaqueNode { fn from_jsmanaged(node: &JS<Node>) -> OpaqueNode {
unsafe { unsafe {
let ptr: uintptr_t = cast::transmute(node.reflector().get_jsobject()); let ptr: uintptr_t = cast::transmute(node.reflector().get_jsobject());
@ -241,9 +253,7 @@ impl OpaqueNode {
} }
} }
/// Converts this node to an `UntrustedNodeAddress`. An `UntrustedNodeAddress` is just the type fn to_untrusted_node_address(&self) -> UntrustedNodeAddress {
/// of node that script expects to receive in a hit test.
pub fn to_untrusted_node_address(&self) -> UntrustedNodeAddress {
unsafe { unsafe {
let OpaqueNode(addr) = *self; let OpaqueNode(addr) = *self;
let addr: UntrustedNodeAddress = cast::transmute(addr); let addr: UntrustedNodeAddress = cast::transmute(addr);
@ -251,11 +261,5 @@ impl OpaqueNode {
} }
} }
/// Returns the address of this node, for debugging purposes.
pub fn id(&self) -> uintptr_t {
unsafe {
cast::transmute_copy(self)
}
}
} }

View file

@ -50,7 +50,7 @@ use std::cast;
use std::cell::{Ref, RefMut}; use std::cell::{Ref, RefMut};
use style::{PropertyDeclarationBlock, TElement, TNode, AttrSelector, SpecificNamespace}; use style::{PropertyDeclarationBlock, TElement, TNode, AttrSelector, SpecificNamespace};
use style::{AnyNamespace}; use style::{AnyNamespace};
use style::computed_values::{content, display};
use layout::util::LayoutDataWrapper; use layout::util::LayoutDataWrapper;
/// Allows some convenience methods on generic layout nodes. /// Allows some convenience methods on generic layout nodes.
@ -58,8 +58,9 @@ pub trait TLayoutNode {
/// Creates a new layout node with the same lifetime as this layout node. /// Creates a new layout node with the same lifetime as this layout node.
unsafe fn new_with_this_lifetime(&self, node: &JS<Node>) -> Self; unsafe fn new_with_this_lifetime(&self, node: &JS<Node>) -> Self;
/// Returns the type ID of this node. Fails if this node is borrowed mutably. /// Returns the type ID of this node. Fails if this node is borrowed mutably. Returns `None`
fn type_id(&self) -> NodeTypeId; /// if this is a pseudo-element; otherwise, returns `Some`.
fn type_id(&self) -> Option<NodeTypeId>;
/// Returns the interior of this node as a `JS`. This is highly unsafe for layout to /// Returns the interior of this node as a `JS`. This is highly unsafe for layout to
/// call and as such is marked `unsafe`. /// call and as such is marked `unsafe`.
@ -73,14 +74,14 @@ pub trait TLayoutNode {
fn node_is_element(&self) -> bool { fn node_is_element(&self) -> bool {
match self.type_id() { match self.type_id() {
ElementNodeTypeId(..) => true, Some(ElementNodeTypeId(..)) => true,
_ => false _ => false
} }
} }
fn node_is_document(&self) -> bool { fn node_is_document(&self) -> bool {
match self.type_id() { match self.type_id() {
DocumentNodeTypeId(..) => true, Some(DocumentNodeTypeId(..)) => true,
_ => false _ => false
} }
} }
@ -114,22 +115,10 @@ pub trait TLayoutNode {
/// If this is a text node, copies out the text. If this is not a text node, fails. /// If this is a text node, copies out the text. If this is not a text node, fails.
/// ///
/// FIXME(pcwalton): Don't copy text. Atomically reference count instead. /// FIXME(pcwalton): Don't copy text. Atomically reference count instead.
fn text(&self) -> ~str { fn text(&self) -> ~str;
unsafe {
if !self.get().is_text() {
fail!("not text!")
}
let text: JS<Text> = self.get_jsmanaged().transmute_copy();
(*text.unsafe_get()).characterdata.data.to_str()
}
}
/// Returns the first child of this node. /// Returns the first child of this node.
fn first_child(&self) -> Option<Self> { fn first_child(&self) -> Option<Self>;
unsafe {
self.get().first_child_ref().map(|node| self.new_with_this_lifetime(node))
}
}
/// Dumps this node tree, for debugging. /// Dumps this node tree, for debugging.
fn dump(&self) { fn dump(&self) {
@ -144,7 +133,7 @@ pub struct LayoutNode<'a> {
priv node: JS<Node>, priv node: JS<Node>,
/// Being chained to a value prevents `LayoutNode`s from escaping. /// Being chained to a value prevents `LayoutNode`s from escaping.
priv chain: &'a (), chain: &'a (),
} }
impl<'ln> Clone for LayoutNode<'ln> { impl<'ln> Clone for LayoutNode<'ln> {
@ -172,12 +161,30 @@ impl<'ln> TLayoutNode for LayoutNode<'ln> {
chain: self.chain, chain: self.chain,
} }
} }
fn type_id(&self) -> NodeTypeId {
self.node.type_id() fn type_id(&self) -> Option<NodeTypeId> {
Some(self.node.type_id())
} }
unsafe fn get_jsmanaged<'a>(&'a self) -> &'a JS<Node> { unsafe fn get_jsmanaged<'a>(&'a self) -> &'a JS<Node> {
&self.node &self.node
} }
fn first_child(&self) -> Option<LayoutNode<'ln>> {
unsafe {
self.get().first_child_ref().map(|node| self.new_with_this_lifetime(node))
}
}
fn text(&self) -> ~str {
unsafe {
if !self.get().is_text() {
fail!("not text!")
}
let text: JS<Text> = self.get_jsmanaged().transmute_copy();
(*text.unsafe_get()).characterdata.data.to_str()
}
}
} }
impl<'ln> LayoutNode<'ln> { impl<'ln> LayoutNode<'ln> {
@ -205,6 +212,10 @@ impl<'ln> LayoutNode<'ln> {
current_node: self.first_child(), current_node: self.first_child(),
} }
} }
pub unsafe fn get_jsmanaged<'a>(&'a self) -> &'a JS<Node> {
&self.node
}
} }
impl<'ln> TNode<LayoutElement<'ln>> for LayoutNode<'ln> { impl<'ln> TNode<LayoutElement<'ln>> for LayoutNode<'ln> {
@ -371,29 +382,107 @@ impl<'le> TElement for LayoutElement<'le> {
} }
} }
fn get_content(content_list: &content::T) -> ~str {
match *content_list {
content::Content(ref value) => {
let iter = &mut value.clone().move_iter().peekable();
match iter.next() {
Some(content::StringContent(content)) => content,
_ => ~"",
}
}
_ => ~"",
}
}
#[deriving(Eq, Clone)]
pub enum ElementType {
Normal,
Before,
After,
BeforeBlock,
AfterBlock,
}
/// A thread-safe version of `LayoutNode`, used during flow construction. This type of layout /// A thread-safe version of `LayoutNode`, used during flow construction. This type of layout
/// node does not allow any parents or siblings of nodes to be accessed, to avoid races. /// node does not allow any parents or siblings of nodes to be accessed, to avoid races.
pub struct ThreadSafeLayoutNode<'ln> { pub struct ThreadSafeLayoutNode<'ln> {
/// The wrapped node. /// The wrapped node.
priv node: JS<Node>, priv node: LayoutNode<'ln>,
/// Being chained to a value prevents `ThreadSafeLayoutNode`s from escaping. priv pseudo: ElementType,
priv chain: &'ln (),
} }
impl<'ln> TLayoutNode for ThreadSafeLayoutNode<'ln> { impl<'ln> TLayoutNode for ThreadSafeLayoutNode<'ln> {
/// Creates a new layout node with the same lifetime as this layout node. /// Creates a new layout node with the same lifetime as this layout node.
unsafe fn new_with_this_lifetime(&self, node: &JS<Node>) -> ThreadSafeLayoutNode<'ln> { unsafe fn new_with_this_lifetime(&self, node: &JS<Node>) -> ThreadSafeLayoutNode<'ln> {
ThreadSafeLayoutNode { ThreadSafeLayoutNode {
node: node.transmute_copy(), node: LayoutNode {
chain: self.chain, node: node.transmute_copy(),
chain: self.node.chain,
},
pseudo: Normal,
} }
} }
fn type_id(&self) -> NodeTypeId {
/// Returns `None` if this is a pseudo-element.
fn type_id(&self) -> Option<NodeTypeId> {
if self.pseudo == Before || self.pseudo == After {
return None
}
self.node.type_id() self.node.type_id()
} }
unsafe fn get_jsmanaged<'a>(&'a self) -> &'a JS<Node> { unsafe fn get_jsmanaged<'a>(&'a self) -> &'a JS<Node> {
&self.node self.node.get_jsmanaged()
}
unsafe fn get<'a>(&'a self) -> &'a Node { // this change.
cast::transmute::<*mut Node,&'a Node>(self.get_jsmanaged().unsafe_get())
}
fn first_child(&self) -> Option<ThreadSafeLayoutNode<'ln>> {
if self.pseudo == Before || self.pseudo == After {
return None
}
if self.has_before_pseudo() {
if self.is_block(Before) && self.pseudo == Normal {
let pseudo_before_node = ThreadSafeLayoutNode::new_with_pseudo_without_self(&self.node, BeforeBlock);
return Some(pseudo_before_node)
} else if self.pseudo == Normal || self.pseudo == BeforeBlock {
let pseudo_before_node = ThreadSafeLayoutNode::new_with_pseudo_without_self(&self.node, Before);
return Some(pseudo_before_node)
}
}
unsafe {
self.get().first_child_ref().map(|node| self.new_with_this_lifetime(node))
}
}
fn text(&self) -> ~str {
if self.pseudo == Before || self.pseudo == After {
let layout_data_ref = self.borrow_layout_data();
let node_layout_data_wrapper = layout_data_ref.get().get_ref();
if self.pseudo == Before {
let before_style = node_layout_data_wrapper.data.before_style.get_ref();
return get_content(&before_style.get().Box.get().content)
} else {
let after_style = node_layout_data_wrapper.data.after_style.get_ref();
return get_content(&after_style.get().Box.get().content)
}
}
unsafe {
if !self.get().is_text() {
fail!("not text!")
}
let text: JS<Text> = self.get_jsmanaged().transmute_copy();
(*text.unsafe_get()).characterdata.data.to_str()
}
} }
} }
@ -401,7 +490,7 @@ impl<'ln> Clone for ThreadSafeLayoutNode<'ln> {
fn clone(&self) -> ThreadSafeLayoutNode<'ln> { fn clone(&self) -> ThreadSafeLayoutNode<'ln> {
ThreadSafeLayoutNode { ThreadSafeLayoutNode {
node: self.node.clone(), node: self.node.clone(),
chain: self.chain, pseudo: self.pseudo,
} }
} }
} }
@ -410,13 +499,33 @@ impl<'ln> ThreadSafeLayoutNode<'ln> {
/// Creates a new `ThreadSafeLayoutNode` from the given `LayoutNode`. /// Creates a new `ThreadSafeLayoutNode` from the given `LayoutNode`.
pub fn new<'a>(node: &LayoutNode<'a>) -> ThreadSafeLayoutNode<'a> { pub fn new<'a>(node: &LayoutNode<'a>) -> ThreadSafeLayoutNode<'a> {
ThreadSafeLayoutNode { ThreadSafeLayoutNode {
node: node.node.clone(), node: node.clone(),
chain: node.chain, pseudo: Normal,
}
}
pub fn new_with_pseudo_without_self<'a>(node: &LayoutNode<'a>, element_type: ElementType) -> ThreadSafeLayoutNode<'a> {
ThreadSafeLayoutNode {
node: node.clone(),
pseudo: element_type,
}
}
/// Creates a new `ThreadSafeLayoutNode` from the given `LayoutNode`.
pub fn new_with_pseudo<'a>(&'a self, element_type: ElementType) -> ThreadSafeLayoutNode<'a> {
ThreadSafeLayoutNode {
node: self.node.clone(),
pseudo: element_type,
} }
} }
/// Returns the next sibling of this node. Unsafe and private because this can lead to races. /// Returns the next sibling of this node. Unsafe and private because this can lead to races.
unsafe fn next_sibling(&self) -> Option<ThreadSafeLayoutNode<'ln>> { unsafe fn next_sibling(&self) -> Option<ThreadSafeLayoutNode<'ln>> {
if self.pseudo == Before || self.pseudo == BeforeBlock {
return self.get().first_child_ref().map(|node| self.new_with_this_lifetime(node))
}
self.node.get().next_sibling_ref().map(|node| self.new_with_this_lifetime(node)) self.node.get().next_sibling_ref().map(|node| self.new_with_this_lifetime(node))
} }
@ -424,6 +533,7 @@ impl<'ln> ThreadSafeLayoutNode<'ln> {
pub fn children(&self) -> ThreadSafeLayoutNodeChildrenIterator<'ln> { pub fn children(&self) -> ThreadSafeLayoutNodeChildrenIterator<'ln> {
ThreadSafeLayoutNodeChildrenIterator { ThreadSafeLayoutNodeChildrenIterator {
current_node: self.first_child(), current_node: self.first_child(),
parent_node: Some(ThreadSafeLayoutNode::new_with_pseudo_without_self(&self.node, self.pseudo)),
} }
} }
@ -431,7 +541,7 @@ impl<'ln> ThreadSafeLayoutNode<'ln> {
#[inline] #[inline]
pub fn as_element(&self) -> ThreadSafeLayoutElement { pub fn as_element(&self) -> ThreadSafeLayoutElement {
unsafe { unsafe {
let elem: JS<Element> = self.node.transmute_copy(); let elem: JS<Element> = self.node.get_jsmanaged().transmute_copy();
let element = elem.unsafe_get(); let element = elem.unsafe_get();
// FIXME(pcwalton): Workaround until Rust gets multiple lifetime parameters on // FIXME(pcwalton): Workaround until Rust gets multiple lifetime parameters on
// implementations. // implementations.
@ -441,6 +551,44 @@ impl<'ln> ThreadSafeLayoutNode<'ln> {
} }
} }
pub fn get_element_type(&self) -> ElementType {
self.pseudo
}
pub fn is_block(&self, kind: ElementType) -> bool {
let mut layout_data_ref = self.mutate_layout_data();
let node_layout_data_wrapper = layout_data_ref.get().get_mut_ref();
let display = match kind {
Before | BeforeBlock => {
let before_style = node_layout_data_wrapper.data.before_style.get_ref();
before_style.get().Box.get().display
}
After | AfterBlock => {
let after_style = node_layout_data_wrapper.data.after_style.get_ref();
after_style.get().Box.get().display
}
Normal => {
let after_style = node_layout_data_wrapper.data.style.get_ref();
after_style.get().Box.get().display
}
};
display == display::block
}
pub fn has_before_pseudo(&self) -> bool {
let layout_data_wrapper = self.borrow_layout_data();
let layout_data_wrapper_ref = layout_data_wrapper.get().get_ref();
layout_data_wrapper_ref.data.before_style.is_some()
}
pub fn has_after_pseudo(&self) -> bool {
let layout_data_wrapper = self.borrow_layout_data();
let layout_data_wrapper_ref = layout_data_wrapper.get().get_ref();
layout_data_wrapper_ref.data.after_style.is_some()
}
/// Borrows the layout data immutably. Fails on a conflicting borrow. /// Borrows the layout data immutably. Fails on a conflicting borrow.
#[inline(always)] #[inline(always)]
pub fn borrow_layout_data<'a>(&'a self) -> Ref<'a,Option<LayoutDataWrapper>> { pub fn borrow_layout_data<'a>(&'a self) -> Ref<'a,Option<LayoutDataWrapper>> {
@ -487,16 +635,56 @@ impl<'ln> ThreadSafeLayoutNode<'ln> {
pub struct ThreadSafeLayoutNodeChildrenIterator<'a> { pub struct ThreadSafeLayoutNodeChildrenIterator<'a> {
priv current_node: Option<ThreadSafeLayoutNode<'a>>, priv current_node: Option<ThreadSafeLayoutNode<'a>>,
priv parent_node: Option<ThreadSafeLayoutNode<'a>>,
} }
impl<'a> Iterator<ThreadSafeLayoutNode<'a>> for ThreadSafeLayoutNodeChildrenIterator<'a> { impl<'a> Iterator<ThreadSafeLayoutNode<'a>> for ThreadSafeLayoutNodeChildrenIterator<'a> {
fn next(&mut self) -> Option<ThreadSafeLayoutNode<'a>> { fn next(&mut self) -> Option<ThreadSafeLayoutNode<'a>> {
let node = self.current_node.clone(); let node = self.current_node.clone();
self.current_node = self.current_node.clone().and_then(|node| {
unsafe { match node {
node.next_sibling() Some(ref node) => {
if node.pseudo == After || node.pseudo == AfterBlock {
return None
}
match self.parent_node {
Some(ref parent_node) => {
if parent_node.pseudo == Normal {
self.current_node = self.current_node.clone().and_then(|node| {
unsafe {
node.next_sibling()
}
});
} else {
self.current_node = None;
}
}
None => {}
}
} }
}); None => {
match self.parent_node {
Some(ref parent_node) => {
if parent_node.has_after_pseudo() {
let pseudo_after_node = if parent_node.is_block(After) && parent_node.pseudo == Normal {
let pseudo_after_node = ThreadSafeLayoutNode::new_with_pseudo_without_self(&parent_node.node, AfterBlock);
Some(pseudo_after_node)
} else if parent_node.pseudo == Normal || parent_node.pseudo == AfterBlock {
let pseudo_after_node = ThreadSafeLayoutNode::new_with_pseudo_without_self(&parent_node.node, After);
Some(pseudo_after_node)
} else {
None
};
self.current_node = pseudo_after_node;
return self.current_node.clone()
}
}
None => {}
}
}
}
node node
} }
} }

View file

@ -9,7 +9,6 @@ use extra::url::Url;
use geom::size::Size2D; use geom::size::Size2D;
use gfx::render_task::{PaintPermissionGranted, PaintPermissionRevoked}; use gfx::render_task::{PaintPermissionGranted, PaintPermissionRevoked};
use gfx::render_task::{RenderChan, RenderTask}; use gfx::render_task::{RenderChan, RenderTask};
use layout::util::OpaqueNode;
use script::layout_interface::LayoutChan; use script::layout_interface::LayoutChan;
use script::script_task::LoadMsg; use script::script_task::LoadMsg;
use script::script_task::{AttachLayoutMsg, NewLayoutInfo, ScriptTask, ScriptChan}; use script::script_task::{AttachLayoutMsg, NewLayoutInfo, ScriptTask, ScriptChan};
@ -28,7 +27,7 @@ pub struct Pipeline {
subpage_id: Option<SubpageId>, subpage_id: Option<SubpageId>,
script_chan: ScriptChan, script_chan: ScriptChan,
layout_chan: LayoutChan, layout_chan: LayoutChan,
render_chan: RenderChan<OpaqueNode>, render_chan: RenderChan,
layout_shutdown_port: Port<()>, layout_shutdown_port: Port<()>,
render_shutdown_port: Port<()>, render_shutdown_port: Port<()>,
/// The most recently loaded url /// The most recently loaded url
@ -40,7 +39,7 @@ pub struct Pipeline {
pub struct CompositionPipeline { pub struct CompositionPipeline {
id: PipelineId, id: PipelineId,
script_chan: ScriptChan, script_chan: ScriptChan,
render_chan: RenderChan<OpaqueNode>, render_chan: RenderChan,
} }
impl Pipeline { impl Pipeline {
@ -171,7 +170,7 @@ impl Pipeline {
subpage_id: Option<SubpageId>, subpage_id: Option<SubpageId>,
script_chan: ScriptChan, script_chan: ScriptChan,
layout_chan: LayoutChan, layout_chan: LayoutChan,
render_chan: RenderChan<OpaqueNode>, render_chan: RenderChan,
layout_shutdown_port: Port<()>, layout_shutdown_port: Port<()>,
render_shutdown_port: Port<()>) render_shutdown_port: Port<()>)
-> Pipeline { -> Pipeline {

View file

@ -2,17 +2,18 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use azure::azure_hl::Color;
use geom::point::Point2D; use geom::point::Point2D;
use geom::rect::Rect; use geom::rect::Rect;
use geom::size::Size2D; use geom::size::Size2D;
use azure::azure_hl::Color;
use layers::platform::surface::{NativeGraphicsMetadata, NativePaintingGraphicsContext}; use layers::platform::surface::{NativeGraphicsMetadata, NativePaintingGraphicsContext};
use layers::platform::surface::{NativeSurface, NativeSurfaceMethods}; use layers::platform::surface::{NativeSurface, NativeSurfaceMethods};
use serialize::{Encoder, Encodable};
use std::fmt::{Formatter, Show};
use std::fmt;
use constellation_msg::PipelineId; use constellation_msg::PipelineId;
use serialize::{Encoder, Encodable};
pub struct LayerBuffer { pub struct LayerBuffer {
/// The native surface which can be shared between threads or processes. On Mac this is an /// The native surface which can be shared between threads or processes. On Mac this is an
/// `IOSurface`; on Linux this is an X Pixmap; on Android this is an `EGLImageKHR`. /// `IOSurface`; on Linux this is an X Pixmap; on Android this is an `EGLImageKHR`.
@ -76,15 +77,73 @@ impl Epoch {
} }
} }
#[deriving(Clone, Eq)]
pub struct LayerId(uint, uint);
impl Show for LayerId {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
let LayerId(a, b) = *self;
write!(f.buf, "Layer({}, {})", a, b);
Ok(())
}
}
impl LayerId {
/// FIXME(#2011, pcwalton): This is unfortunate. Maybe remove this in the future.
pub fn null() -> LayerId {
LayerId(0, 0)
}
}
/// The scrolling policy of a layer.
#[deriving(Eq)]
pub enum ScrollPolicy {
/// These layers scroll when the parent receives a scrolling message.
Scrollable,
/// These layers do not scroll when the parent receives a scrolling message.
FixedPosition,
}
/// All layer-specific information that the painting task sends to the compositor other than the
/// buffer contents of the layer itself.
pub struct LayerMetadata {
/// An opaque ID. This is usually the address of the flow and index of the box within it.
id: LayerId,
/// The position and size of the layer in pixels.
position: Rect<uint>,
/// The background color of the layer.
background_color: Color,
/// The scrolling policy of this layer.
scroll_policy: ScrollPolicy,
}
/// The interface used by the renderer to acquire draw targets for each render frame and /// The interface used by the renderer to acquire draw targets for each render frame and
/// submit them to be drawn to the display. /// submit them to be drawn to the display.
pub trait RenderListener { pub trait RenderListener {
fn get_graphics_metadata(&self) -> Option<NativeGraphicsMetadata>; fn get_graphics_metadata(&self) -> Option<NativeGraphicsMetadata>;
fn new_layer(&self, PipelineId, Size2D<uint>); fn create_layer_group_for_pipeline(&self, PipelineId, Size2D<uint>);
fn set_layer_page_size_and_color(&self, PipelineId, Size2D<uint>, Epoch, Color);
fn set_layer_clip_rect(&self, PipelineId, Rect<uint>); /// Informs the compositor of the layers for the given pipeline. The compositor responds by
fn delete_layer(&self, PipelineId); /// creating and/or destroying render layers as necessary.
fn paint(&self, id: PipelineId, layer_buffer_set: ~LayerBufferSet, Epoch); fn initialize_layers_for_pipeline(&self,
pipeline_id: PipelineId,
metadata: ~[LayerMetadata],
epoch: Epoch);
fn set_layer_clip_rect(&self,
pipeline_id: PipelineId,
layer_id: LayerId,
new_rect: Rect<uint>);
fn delete_layer_group(&self, PipelineId);
/// Sends new tiles for the given layer to the compositor.
fn paint(&self,
pipeline_id: PipelineId,
layer_id: LayerId,
layer_buffer_set: ~LayerBufferSet,
epoch: Epoch);
fn set_render_state(&self, render_state: RenderState); fn set_render_state(&self, render_state: RenderState);
} }
@ -92,8 +151,11 @@ pub trait RenderListener {
/// which is used in displaying the appropriate message in the window's title. /// which is used in displaying the appropriate message in the window's title.
pub trait ScriptListener : Clone { pub trait ScriptListener : Clone {
fn set_ready_state(&self, ReadyState); fn set_ready_state(&self, ReadyState);
fn invalidate_rect(&self, PipelineId, Rect<uint>); fn invalidate_rect(&self, pipeline_id: PipelineId, layer_id: LayerId, rect: Rect<uint>);
fn scroll_fragment_point(&self, PipelineId, Point2D<f32>); fn scroll_fragment_point(&self,
pipeline_id: PipelineId,
layer_id: LayerId,
point: Point2D<f32>);
fn close(&self); fn close(&self);
fn dup(&self) -> ~ScriptListener; fn dup(&self) -> ~ScriptListener;
} }

View file

@ -37,7 +37,8 @@ use js::jsapi::{JSObject, JS_InhibitGC, JS_AllowGC, JS_CallFunctionValue};
use js::jsval::NullValue; use js::jsval::NullValue;
use js::rust::{Compartment, Cx, CxUtils, RtUtils}; use js::rust::{Compartment, Cx, CxUtils, RtUtils};
use js; use js;
use servo_msg::compositor_msg::{FinishedLoading, Loading, PerformingLayout, ScriptListener}; use servo_msg::compositor_msg::{FinishedLoading, LayerId, Loading, PerformingLayout};
use servo_msg::compositor_msg::{ScriptListener};
use servo_msg::constellation_msg::{ConstellationChan, IFrameSandboxed, IFrameUnsandboxed}; use servo_msg::constellation_msg::{ConstellationChan, IFrameSandboxed, IFrameUnsandboxed};
use servo_msg::constellation_msg::{LoadIframeUrlMsg, LoadCompleteMsg, LoadUrlMsg, NavigationDirection}; use servo_msg::constellation_msg::{LoadIframeUrlMsg, LoadCompleteMsg, LoadUrlMsg, NavigationDirection};
use servo_msg::constellation_msg::{PipelineId, SubpageId, Failure, FailureMsg}; use servo_msg::constellation_msg::{PipelineId, SubpageId, Failure, FailureMsg};
@ -929,10 +930,15 @@ impl ScriptTask {
fn scroll_fragment_point(&self, pipeline_id: PipelineId, page: &Page, node: JS<Element>) { fn scroll_fragment_point(&self, pipeline_id: PipelineId, page: &Page, node: JS<Element>) {
let (port, chan) = Chan::new(); let (port, chan) = Chan::new();
let node: JS<Node> = NodeCast::from(&node); let node: JS<Node> = NodeCast::from(&node);
let ContentBoxResponse(rect) = page.query_layout(ContentBoxQuery(node.to_trusted_node_address(), chan), port); let ContentBoxResponse(rect) =
page.query_layout(ContentBoxQuery(node.to_trusted_node_address(), chan), port);
let point = Point2D(to_frac_px(rect.origin.x).to_f32().unwrap(), let point = Point2D(to_frac_px(rect.origin.x).to_f32().unwrap(),
to_frac_px(rect.origin.y).to_f32().unwrap()); to_frac_px(rect.origin.y).to_f32().unwrap());
self.compositor.scroll_fragment_point(pipeline_id, point); // FIXME(#2003, pcwalton): This is pretty bogus when multiple layers are involved.
// Really what needs to happen is that this needs to go through layout to ask which
// layer the element belongs to, and have it send the scroll message to the
// compositor.
self.compositor.scroll_fragment_point(pipeline_id, LayerId::null(), point);
} }
/// This is the main entry point for receiving and dispatching DOM events. /// This is the main entry point for receiving and dispatching DOM events.

View file

@ -8,6 +8,7 @@ pub use servo_util::geometry::Au;
pub type CSSFloat = f64; pub type CSSFloat = f64;
pub static DEFAULT_LINE_HEIGHT: CSSFloat = 1.14;
pub mod specified { pub mod specified {
use std::ascii::StrAsciiExt; use std::ascii::StrAsciiExt;
@ -172,7 +173,10 @@ pub mod computed {
color: longhands::color::computed_value::T, color: longhands::color::computed_value::T,
inherited_font_weight: longhands::font_weight::computed_value::T, inherited_font_weight: longhands::font_weight::computed_value::T,
inherited_font_size: longhands::font_size::computed_value::T, inherited_font_size: longhands::font_size::computed_value::T,
inherited_minimum_line_height: longhands::_servo_minimum_line_height::T,
inherited_height: longhands::height::T,
font_size: longhands::font_size::computed_value::T, font_size: longhands::font_size::computed_value::T,
display: longhands::display::computed_value::T,
positioned: bool, positioned: bool,
floated: bool, floated: bool,
border_top_present: bool, border_top_present: bool,

View file

@ -32,10 +32,11 @@ def to_rust_ident(name):
return name return name
class Longhand(object): class Longhand(object):
def __init__(self, name): def __init__(self, name, derived_from=None):
self.name = name self.name = name
self.ident = to_rust_ident(name) self.ident = to_rust_ident(name)
self.style_struct = THIS_STYLE_STRUCT self.style_struct = THIS_STYLE_STRUCT
self.derived_from = None if derived_from is None else to_rust_ident(derived_from)
class Shorthand(object): class Shorthand(object):
def __init__(self, name, sub_properties): def __init__(self, name, sub_properties):
@ -53,6 +54,7 @@ STYLE_STRUCTS = []
THIS_STYLE_STRUCT = None THIS_STYLE_STRUCT = None
LONGHANDS = [] LONGHANDS = []
LONGHANDS_BY_NAME = {} LONGHANDS_BY_NAME = {}
DERIVED_LONGHANDS = {}
SHORTHANDS = [] SHORTHANDS = []
def new_style_struct(name, is_inherited): def new_style_struct(name, is_inherited):
@ -81,12 +83,13 @@ pub mod longhands {
value value
} }
<%def name="raw_longhand(name, no_super=False)"> <%def name="raw_longhand(name, no_super=False, derived_from=None)">
<% <%
property = Longhand(name) property = Longhand(name, derived_from=derived_from)
THIS_STYLE_STRUCT.longhands.append(property) THIS_STYLE_STRUCT.longhands.append(property)
LONGHANDS.append(property) LONGHANDS.append(property)
LONGHANDS_BY_NAME[name] = property LONGHANDS_BY_NAME[name] = property
DERIVED_LONGHANDS.setdefault(derived_from, []).append(property)
%> %>
pub mod ${property.ident} { pub mod ${property.ident} {
% if not no_super: % if not no_super:
@ -94,30 +97,34 @@ pub mod longhands {
% endif % endif
pub use self::computed_value::*; pub use self::computed_value::*;
${caller.body()} ${caller.body()}
pub fn parse_declared(input: &[ComponentValue], base_url: &Url) % if derived_from is None:
-> Option<DeclaredValue<SpecifiedValue>> { pub fn parse_declared(input: &[ComponentValue], base_url: &Url)
match CSSWideKeyword::parse(input) { -> Option<DeclaredValue<SpecifiedValue>> {
Some(Some(keyword)) => Some(CSSWideKeyword(keyword)), match CSSWideKeyword::parse(input) {
Some(None) => Some(CSSWideKeyword(${ Some(Some(keyword)) => Some(CSSWideKeyword(keyword)),
"Inherit" if THIS_STYLE_STRUCT.inherited else "Initial"})), Some(None) => Some(CSSWideKeyword(${
None => parse_specified(input, base_url), "Inherit" if THIS_STYLE_STRUCT.inherited else "Initial"})),
None => parse_specified(input, base_url),
}
} }
} % endif
} }
</%def> </%def>
<%def name="longhand(name, no_super=False)"> <%def name="longhand(name, no_super=False, derived_from=None)">
<%self:raw_longhand name="${name}"> <%self:raw_longhand name="${name}" derived_from="${derived_from}">
${caller.body()} ${caller.body()}
pub fn parse_specified(input: &[ComponentValue], base_url: &Url) % if derived_from is None:
-> Option<DeclaredValue<SpecifiedValue>> { pub fn parse_specified(_input: &[ComponentValue], _base_url: &Url)
parse(input, base_url).map(super::SpecifiedValue) -> Option<DeclaredValue<SpecifiedValue>> {
} parse(_input, _base_url).map(super::SpecifiedValue)
}
% endif
</%self:raw_longhand> </%self:raw_longhand>
</%def> </%def>
<%def name="single_component_value(name)"> <%def name="single_component_value(name, derived_from=None)">
<%self:longhand name="${name}"> <%self:longhand name="${name}" derived_from="${derived_from}">
${caller.body()} ${caller.body()}
pub fn parse(input: &[ComponentValue], base_url: &Url) -> Option<SpecifiedValue> { pub fn parse(input: &[ComponentValue], base_url: &Url) -> Option<SpecifiedValue> {
one_component_value(input).and_then(|c| from_component_value(c, base_url)) one_component_value(input).and_then(|c| from_component_value(c, base_url))
@ -307,9 +314,29 @@ pub mod longhands {
${predefined_type("width", "LengthOrPercentageOrAuto", ${predefined_type("width", "LengthOrPercentageOrAuto",
"computed::LPA_Auto", "computed::LPA_Auto",
"parse_non_negative")} "parse_non_negative")}
${predefined_type("height", "LengthOrPercentageOrAuto", <%self:single_component_value name="height">
"computed::LPA_Auto", pub type SpecifiedValue = specified::LengthOrPercentageOrAuto;
"parse_non_negative")} pub mod computed_value {
pub type T = super::super::computed::LengthOrPercentageOrAuto;
}
#[inline]
pub fn get_initial_value() -> computed_value::T { computed::LPA_Auto }
#[inline]
pub fn from_component_value(v: &ComponentValue, _base_url: &Url)
-> Option<SpecifiedValue> {
specified::LengthOrPercentageOrAuto::parse_non_negative(v)
}
pub fn to_computed_value(value: SpecifiedValue, context: &computed::Context)
-> computed_value::T {
match (value, context.inherited_height) {
(specified::LPA_Percentage(_), computed::LPA_Auto)
if !context.is_root_element && !context.positioned => {
computed::LPA_Auto
},
_ => computed::compute_LengthOrPercentageOrAuto(value, context)
}
}
</%self:single_component_value>
${predefined_type("min-width", "LengthOrPercentage", ${predefined_type("min-width", "LengthOrPercentage",
"computed::LP_Length(Au(0))", "computed::LP_Length(Au(0))",
@ -318,6 +345,13 @@ pub mod longhands {
"computed::LPN_None", "computed::LPN_None",
"parse_non_negative")} "parse_non_negative")}
${predefined_type("min-height", "LengthOrPercentage",
"computed::LP_Length(Au(0))",
"parse_non_negative")}
${predefined_type("max-height", "LengthOrPercentageOrNone",
"computed::LPN_None",
"parse_non_negative")}
${new_style_struct("InheritedBox", is_inherited=True)} ${new_style_struct("InheritedBox", is_inherited=True)}
<%self:single_component_value name="line-height"> <%self:single_component_value name="line-height">
@ -366,6 +400,40 @@ pub mod longhands {
} }
</%self:single_component_value> </%self:single_component_value>
<%self:longhand name="-servo-minimum-line-height" derived_from="line-height">
use super::Au;
use super::super::common_types::DEFAULT_LINE_HEIGHT;
use super::super::longhands::display;
use super::super::longhands::line_height;
pub use to_computed_value = super::computed_as_specified;
pub type SpecifiedValue = line_height::SpecifiedValue;
pub mod computed_value {
pub type T = super::super::Au;
}
#[inline]
pub fn get_initial_value() -> computed_value::T {
Au(0)
}
#[inline]
pub fn derive(value: line_height::computed_value::T, context: &computed::Context)
-> Au {
if context.display != display::computed_value::inline {
match value {
line_height::Normal => context.font_size.scale_by(DEFAULT_LINE_HEIGHT),
line_height::Number(percentage) => context.font_size.scale_by(percentage),
line_height::Length(length) => length,
}
} else {
context.inherited_minimum_line_height
}
}
</%self:longhand>
${switch_to_style_struct("Box")} ${switch_to_style_struct("Box")}
<%self:single_component_value name="vertical-align"> <%self:single_component_value name="vertical-align">
@ -519,6 +587,81 @@ pub mod longhands {
} }
</%self:single_component_value> </%self:single_component_value>
<%self:longhand name="background-position">
use super::super::common_types::specified;
pub mod computed_value {
use super::super::super::common_types::computed::LengthOrPercentage;
#[deriving(Eq, Clone)]
pub struct T {
horizontal: LengthOrPercentage,
vertical: LengthOrPercentage,
}
}
#[deriving(Clone)]
pub struct SpecifiedValue {
horizontal: specified::LengthOrPercentage,
vertical: specified::LengthOrPercentage,
}
#[inline]
pub fn to_computed_value(value: SpecifiedValue, context: &computed::Context)
-> computed_value::T {
computed_value::T {
horizontal: computed::compute_LengthOrPercentage(value.horizontal, context),
vertical: computed::compute_LengthOrPercentage(value.vertical, context),
}
}
#[inline]
pub fn get_initial_value() -> computed_value::T {
computed_value::T {
horizontal: computed::LP_Percentage(0.0),
vertical: computed::LP_Percentage(0.0),
}
}
// FIXME(#1997, pcwalton): Support complete CSS2 syntax.
pub fn parse_horizontal_and_vertical(horiz: &ComponentValue, vert: &ComponentValue)
-> Option<SpecifiedValue> {
let horiz = match specified::LengthOrPercentage::parse_non_negative(horiz) {
None => return None,
Some(value) => value,
};
let vert = match specified::LengthOrPercentage::parse_non_negative(vert) {
None => return None,
Some(value) => value,
};
Some(SpecifiedValue {
horizontal: horiz,
vertical: vert,
})
}
pub fn parse(input: &[ComponentValue], _: &Url) -> Option<SpecifiedValue> {
let mut input_iter = input.skip_whitespace();
let horizontal = input_iter.next();
let vertical = input_iter.next();
if input_iter.next().is_some() {
return None
}
match (horizontal, vertical) {
(Some(horizontal), Some(vertical)) => {
parse_horizontal_and_vertical(horizontal, vertical)
}
_ => None
}
}
</%self:longhand>
${single_keyword("background-repeat", "repeat repeat-x repeat-y no-repeat")}
${single_keyword("background-attachment", "scroll fixed")}
${new_style_struct("Color", is_inherited=True)} ${new_style_struct("Color", is_inherited=True)}
@ -866,29 +1009,97 @@ pub mod shorthands {
</%def> </%def>
// TODO: other background-* properties // TODO: other background-* properties
<%self:shorthand name="background" sub_properties="background-color background-image"> <%self:shorthand name="background"
let mut color = None; sub_properties="background-color background-position background-repeat background-attachment background-image">
let mut image = None; use std::mem;
let (mut color, mut image, mut position, mut repeat, mut attachment) =
(None, None, None, None, None);
let mut last_component_value = None;
let mut any = false; let mut any = false;
for component_value in input.skip_whitespace() { for component_value in input.skip_whitespace() {
if color.is_none() { if color.is_none() {
match background_color::from_component_value(component_value, base_url) { match background_color::from_component_value(component_value, base_url) {
Some(v) => { color = Some(v); any = true; continue }, Some(v) => {
color = Some(v);
any = true;
continue
},
None => () None => ()
} }
} }
if image.is_none() { if image.is_none() {
match background_image::from_component_value(component_value, base_url) { match background_image::from_component_value(component_value, base_url) {
Some(v) => { image = Some(v); any = true; continue }, Some(v) => {
image = Some(v);
any = true;
continue
},
None => (), None => (),
} }
} }
return None;
if repeat.is_none() {
match background_repeat::from_component_value(component_value, base_url) {
Some(v) => {
repeat = Some(v);
any = true;
continue
},
None => ()
}
}
if attachment.is_none() {
match background_attachment::from_component_value(component_value,
base_url) {
Some(v) => {
attachment = Some(v);
any = true;
continue
},
None => ()
}
}
match mem::replace(&mut last_component_value, None) {
Some(saved_component_value) => {
if position.is_none() {
match background_position::parse_horizontal_and_vertical(
saved_component_value,
component_value) {
Some(v) => {
position = Some(v);
any = true;
continue
},
None => (),
}
}
// If we get here, parsing failed.
return None
}
None => {
// Save the component value.
last_component_value = Some(component_value)
}
}
}
if any && last_component_value.is_none() {
Some(Longhands {
background_color: color,
background_image: image,
background_position: position,
background_repeat: repeat,
background_attachment: attachment,
})
} else {
None
} }
if any { Some(Longhands { background_color: color, background_image: image }) }
else { None }
</%self:shorthand> </%self:shorthand>
${four_sides_shorthand("margin", "margin-%s", "margin_top::from_component_value")} ${four_sides_shorthand("margin", "margin-%s", "margin_top::from_component_value")}
@ -1137,12 +1348,16 @@ impl PropertyDeclaration {
let name_lower = tmp_for_lifetime.as_str_ascii(); let name_lower = tmp_for_lifetime.as_str_ascii();
match name_lower.as_slice() { match name_lower.as_slice() {
% for property in LONGHANDS: % for property in LONGHANDS:
"${property.name}" => result_list.push(${property.ident}_declaration( % if property.derived_from is None:
match longhands::${property.ident}::parse_declared(value, base_url) { "${property.name}" => result_list.push(${property.ident}_declaration(
Some(value) => value, match longhands::${property.ident}::parse_declared(value, base_url) {
None => return InvalidValue, Some(value) => value,
} None => return InvalidValue,
)), }
)),
% else:
"${property.name}" => {}
% endif
% endfor % endfor
% for shorthand in SHORTHANDS: % for shorthand in SHORTHANDS:
"${shorthand.name}" => match CSSWideKeyword::parse(value) { "${shorthand.name}" => match CSSWideKeyword::parse(value) {
@ -1254,26 +1469,44 @@ fn cascade_with_cached_declarations(applicable_declarations: &[MatchedProperty],
% for style_struct in STYLE_STRUCTS: % for style_struct in STYLE_STRUCTS:
% if style_struct.inherited: % if style_struct.inherited:
% for property in style_struct.longhands: % for property in style_struct.longhands:
${property.ident}_declaration(ref declared_value) => { % if property.derived_from is None:
style_${style_struct.name}.get_mut().${property.ident} = ${property.ident}_declaration(ref declared_value) => {
match *declared_value { let computed_value = match *declared_value {
SpecifiedValue(ref specified_value) SpecifiedValue(ref specified_value)
=> longhands::${property.ident}::to_computed_value( => longhands::${property.ident}::to_computed_value(
(*specified_value).clone(), (*specified_value).clone(),
context context
), ),
CSSWideKeyword(Initial) CSSWideKeyword(Initial)
=> longhands::${property.ident}::get_initial_value(), => longhands::${property.ident}::get_initial_value(),
CSSWideKeyword(Inherit) => { CSSWideKeyword(Inherit) => {
// This is a bit slow, but this is rare so it shouldn't matter. // This is a bit slow, but this is rare so it shouldn't
// FIXME: is it still? // matter.
parent_style.${style_struct.name} //
.get() // FIXME: is it still?
.${property.ident} parent_style.${style_struct.name}
.clone() .get()
} .${property.ident}
.clone()
}
};
style_${style_struct.name}.get_mut().${property.ident} =
computed_value;
% if property.name in DERIVED_LONGHANDS:
% for derived in DERIVED_LONGHANDS[property.name]:
style_${derived.style_struct.name}.get_mut()
.${derived.ident} =
longhands::${derived.ident}::derive(computed_value,
context);
% endfor
% endif
} }
} % else:
${property.ident}_declaration(_) => {
// Ignore derived properties; they cannot be set by content.
}
% endif
% endfor % endfor
% endif % endif
% endfor % endfor
@ -1325,8 +1558,13 @@ pub fn cascade(applicable_declarations: &[MatchedProperty],
is_root_element: is_root_element, is_root_element: is_root_element,
inherited_font_weight: inherited_font_style.font_weight, inherited_font_weight: inherited_font_style.font_weight,
inherited_font_size: inherited_font_style.font_size, inherited_font_size: inherited_font_style.font_size,
inherited_height: inherited_style.Box.get().height,
inherited_minimum_line_height: inherited_style.InheritedBox
.get()
._servo_minimum_line_height,
// To be overridden by applicable declarations: // To be overridden by applicable declarations:
font_size: inherited_font_style.font_size, font_size: inherited_font_style.font_size,
display: longhands::display::get_initial_value(),
color: inherited_style.Color.get().color, color: inherited_style.Color.get().color,
positioned: false, positioned: false,
floated: false, floated: false,
@ -1363,6 +1601,9 @@ pub fn cascade(applicable_declarations: &[MatchedProperty],
color_declaration(ref value) => { color_declaration(ref value) => {
context.color = get_specified!(Color, color, value); context.color = get_specified!(Color, color, value);
} }
display_declaration(ref value) => {
context.display = get_specified!(Box, display, value);
}
position_declaration(ref value) => { position_declaration(ref value) => {
context.positioned = match get_specified!(Box, position, value) { context.positioned = match get_specified!(Box, position, value) {
longhands::position::absolute | longhands::position::fixed => true, longhands::position::absolute | longhands::position::fixed => true,
@ -1414,27 +1655,45 @@ pub fn cascade(applicable_declarations: &[MatchedProperty],
match *declaration { match *declaration {
% for style_struct in STYLE_STRUCTS: % for style_struct in STYLE_STRUCTS:
% for property in style_struct.longhands: % for property in style_struct.longhands:
${property.ident}_declaration(ref declared_value) => { % if property.derived_from is None:
style_${style_struct.name}.get_mut().${property.ident} = ${property.ident}_declaration(ref declared_value) => {
match *declared_value { let computed_value = match *declared_value {
SpecifiedValue(ref specified_value) SpecifiedValue(ref specified_value)
=> longhands::${property.ident}::to_computed_value( => longhands::${property.ident}::to_computed_value(
(*specified_value).clone(), (*specified_value).clone(),
&context &context
), ),
CSSWideKeyword(Initial) CSSWideKeyword(Initial)
=> longhands::${property.ident}::get_initial_value(), => longhands::${property.ident}::get_initial_value(),
CSSWideKeyword(Inherit) => { CSSWideKeyword(Inherit) => {
// This is a bit slow, but this is rare so it shouldn't matter. // This is a bit slow, but this is rare so it shouldn't
// FIXME: is it still? // matter.
cacheable = false; //
inherited_style.${style_struct.name} // FIXME: is it still?
.get() cacheable = false;
.${property.ident} inherited_style.${style_struct.name}
.clone() .get()
} .${property.ident}
.clone()
}
};
style_${style_struct.name}.get_mut().${property.ident} =
computed_value;
% if property.name in DERIVED_LONGHANDS:
% for derived in DERIVED_LONGHANDS[property.name]:
style_${derived.style_struct.name}.get_mut()
.${derived.ident} =
longhands::${derived.ident}::derive(computed_value,
&context);
% endfor
% endif
} }
} % else:
${property.ident}_declaration(_) => {
// Ignore derived properties; they cannot be set by content.
}
% endif
% endfor % endfor
% endfor % endfor
} }

View file

@ -300,3 +300,11 @@ pub fn to_pt(au: Au) -> f64 {
(a as f64) / 60f64 * 72f64 / 96f64 (a as f64) / 60f64 * 72f64 / 96f64
} }
/// Returns true if the rect contains the given point. Points on the top or left sides of the rect
/// are considered inside the rectangle, while points on the right or bottom sides of the rect are
/// not considered inside the rectangle.
pub fn rect_contains_point<T:Ord + Add<T,T>>(rect: Rect<T>, point: Point2D<T>) -> bool {
point.x >= rect.origin.x && point.x < rect.origin.x + rect.size.width &&
point.y >= rect.origin.y && point.y < rect.origin.y + rect.size.height
}

View file

@ -60,6 +60,16 @@ pub trait SmallVec<T> : SmallVecPrivate<T> {
} }
} }
fn mut_iter<'a>(&'a mut self) -> SmallVecMutIterator<'a,T> {
unsafe {
SmallVecMutIterator {
ptr: cast::transmute(self.begin()),
end: cast::transmute(self.end()),
lifetime: None,
}
}
}
/// NB: For efficiency reasons (avoiding making a second copy of the inline elements), this /// NB: For efficiency reasons (avoiding making a second copy of the inline elements), this
/// actually clears out the original array instead of moving it. /// actually clears out the original array instead of moving it.
fn move_iter<'a>(&'a mut self) -> SmallVecMoveIterator<'a,T> { fn move_iter<'a>(&'a mut self) -> SmallVecMoveIterator<'a,T> {
@ -94,6 +104,12 @@ pub trait SmallVec<T> : SmallVecPrivate<T> {
} }
} }
fn push_all_move<V:SmallVec<T>>(&mut self, mut other: V) {
for value in other.move_iter() {
self.push(value)
}
}
fn grow(&mut self, new_cap: uint) { fn grow(&mut self, new_cap: uint) {
unsafe { unsafe {
let new_alloc: *mut T = cast::transmute(global_heap::malloc_raw(mem::size_of::<T>() * let new_alloc: *mut T = cast::transmute(global_heap::malloc_raw(mem::size_of::<T>() *
@ -194,6 +210,30 @@ impl<'a,T> Iterator<&'a T> for SmallVecIterator<'a,T> {
} }
} }
pub struct SmallVecMutIterator<'a,T> {
priv ptr: *mut T,
priv end: *mut T,
priv lifetime: Option<&'a mut T>
}
impl<'a,T> Iterator<&'a mut T> for SmallVecMutIterator<'a,T> {
#[inline]
fn next(&mut self) -> Option<&'a mut T> {
unsafe {
if self.ptr == self.end {
return None
}
let old = self.ptr;
self.ptr = if mem::size_of::<T>() == 0 {
cast::transmute(self.ptr as uint + 1)
} else {
self.ptr.offset(1)
};
Some(cast::transmute(old))
}
}
}
pub struct SmallVecMoveIterator<'a,T> { pub struct SmallVecMoveIterator<'a,T> {
priv allocation: Option<*mut u8>, priv allocation: Option<*mut u8>,
priv iter: SmallVecIterator<'static,T>, priv iter: SmallVecIterator<'static,T>,

@ -1 +1 @@
Subproject commit cca153ff06af97b3a66586be65322ca3196e60ff Subproject commit 10b1cf8e35fa1757bcab11dcc284f77e4ceeaffc

@ -1 +1 @@
Subproject commit 400a01307515def5dd6874ec44ed8a017ada36fd Subproject commit 79e405fa59c052ff78d7a2527a92474a32ac9b4d

View file

@ -5,6 +5,6 @@
<link rel="stylesheet" href="subdirectory/background_image.css"> <link rel="stylesheet" href="subdirectory/background_image.css">
</head> </head>
<body> <body>
<div class="test" style="width:200px; height:200px;"></div> <div class="test" style="width:206px; height:206px;"></div>
</body> </body>
</html> </html>

View file

@ -0,0 +1,20 @@
<!DOCTYPE html>
<html>
<head>
<title>You see here a scroll labeled FOOBIE BLETCH.</title>
<style>
#foo {
background: url(400x400_green.png);
background-position: 128px 0px;
background-repeat: no-repeat;
width: 528px;
height: 400px;
margin-left: 0;
}
</style>
</head>
<body>
<div id=foo></div>
</body>
</html>

View file

@ -0,0 +1,19 @@
<!DOCTYPE html>
<html>
<head>
<title>You see here a scroll labeled FOOBIE BLETCH.</title>
<style>
#foo {
background: url(400x400_green.png);
width: 400px;
height: 400px;
margin-left: 128px;
}
</style>
</head>
<body>
<div id=foo></div>
</body>
</html>

View file

@ -4,6 +4,6 @@
<title></title> <title></title>
</head> </head>
<body> <body>
<img class="test" src="rust-0.png" style="width:200px; height:200px;" /> <img class="test" src="rust-0.png" style="width:206px; height:206px;" />
</body> </body>
</html> </html>

View file

@ -0,0 +1,19 @@
<!DOCTYPE html>
<html>
<head>
<title>You see here a scroll labeled XIXAXA XOXAXA XUXAXA.</title>
<style>
div {
width: 412px;
height: 412px;
background: url(rust-0.png);
background-repeat: repeat;
}
</style>
</head>
<body>
<div id=repeat></div>
</body>
</html>

View file

@ -0,0 +1,27 @@
<!DOCTYPE html>
<html>
<head>
<title>You see here a scroll labeled XIXAXA XOXAXA XUXAXA.</title>
<style>
.repeaty {
width: 412px;
height: 206px;
background: url(rust-0.png);
}
.repeatx {
width: 206px;
height: 206px;
background: url(rust-0.png);
float: left;
}
</style>
</head>
<body>
<div>
<div class=repeaty><div class=repeatx></div><div class=repeatx></div></div>
<div class=repeaty><div class=repeatx></div><div class=repeatx></div></div>
</div>
</body>
</html>

View file

@ -0,0 +1,19 @@
<!DOCTYPE html>
<html>
<head>
<title>You see here a scroll labeled XIXAXA XOXAXA XUXAXA.</title>
<style>
div {
width: 400px;
height: 400px;
background: url(rust-0.png);
background-repeat: no-repeat;
}
</style>
</head>
<body>
<div id=repeat></div>
</body>
</html>

View file

@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<title>You see here a scroll labeled XIXAXA XOXAXA XUXAXA.</title>
<style>
div {
width: 200px;
height: 200px;
background: url(rust-0.png);
}
</style>
</head>
<body>
<div id=repeat></div>
</body>
</html>

View file

@ -0,0 +1,22 @@
<!DOCTYPE html>
<html>
<head>
<title>You see here a scroll labeled XIXAXA XOXAXA XUXAXA.</title>
<style>
div {
width: 412px;
height: 412px;
background: url(rust-0.png);
background-repeat: repeat-x;
position: absolute;
left: 0;
top: 0;
}
</style>
</head>
<body>
<div></div>
</body>
</html>

View file

@ -0,0 +1,26 @@
<!DOCTYPE html>
<html>
<head>
<title>You see here a scroll labeled XIXAXA XOXAXA XUXAXA.</title>
<style>
div {
width: 206px;
height: 206px;
background: url(rust-0.png);
position: absolute;
top: 0;
}
.repeata {
left: 0;
}
.repeatb {
left: 206px;
}
</style>
</head>
<body>
<div class=repeata></div><div class=repeatb></div>
</body>
</html>

View file

@ -0,0 +1,19 @@
<!DOCTYPE html>
<html>
<head>
<title>You see here a scroll labeled XIXAXA XOXAXA XUXAXA.</title>
<style>
div {
width: 412px;
height: 412px;
background: url(rust-0.png);
background-repeat: repeat-y;
}
</style>
</head>
<body>
<div id=repeat></div>
</body>
</html>

View file

@ -0,0 +1,21 @@
<!DOCTYPE html>
<html>
<head>
<title>You see here a scroll labeled XIXAXA XOXAXA XUXAXA.</title>
<style>
.repeaty {
width: 206px;
height: 206px;
background: url(rust-0.png);
}
</style>
</head>
<body>
<div>
<div class=repeaty></div>
<div class=repeaty></div>
</div>
</body>
</html>

View file

@ -4,6 +4,6 @@
<title></title> <title></title>
</head> </head>
<body> <body>
<div class="test" style="background: url(rust-0.png); width:200px; height:200px;"></div> <div class="test" style="background: url(rust-0.png); width:206px; height:206px;"></div>
</body> </body>
</html> </html>

View file

@ -48,9 +48,20 @@
== position_abs_replaced_simple_a.html position_abs_replaced_simple_b.html == position_abs_replaced_simple_a.html position_abs_replaced_simple_b.html
== position_abs_static_y_a.html position_abs_static_y_b.html == position_abs_static_y_a.html position_abs_static_y_b.html
== position_abs_width_percentage_a.html position_abs_width_percentage_b.html == position_abs_width_percentage_a.html position_abs_width_percentage_b.html
== position_fixed_a.html position_fixed_b.html # commented out because multiple layers don't work with reftests --pcwalton
== position_fixed_simple_a.html position_fixed_simple_b.html # == position_fixed_a.html position_fixed_b.html
== position_fixed_static_y_a.html position_fixed_static_y_b.html # == position_fixed_simple_a.html position_fixed_simple_b.html
# == position_fixed_static_y_a.html position_fixed_static_y_b.html
== position_relative_a.html position_relative_b.html == position_relative_a.html position_relative_b.html
== position_relative_top_percentage_a.html position_relative_top_percentage_b.html == position_relative_top_percentage_a.html position_relative_top_percentage_b.html
== background_none_a.html background_none_b.html == background_none_a.html background_none_b.html
== negative_margins_a.html negative_margins_b.html
== negative_margin_uncle_a.html negative_margin_uncle_b.html
== inline_padding_a.html inline_padding_b.html
== min_max_height_a.html min_max_height_b.html
== minimum_line_height_a.html minimum_line_height_b.html
== background_position_a.html background_position_b.html
== background_repeat_x_a.html background_repeat_x_b.html
== background_repeat_y_a.html background_repeat_y_b.html
== background_repeat_none_a.html background_repeat_none_b.html
== background_repeat_both_a.html background_repeat_both_b.html

View file

@ -0,0 +1,16 @@
<!DOCTYPE html>
<html>
<head>
<title>You see here a scroll labeled READ ME.</title>
<style>
#a {
padding: 0 0 0 64px;
color: blue;
}
</style>
</head>
<body>
<div><span id=a>blah blah blah</span></div>
</body>
</html>

View file

@ -0,0 +1,17 @@
<!DOCTYPE html>
<html>
<head>
<title>You see here a scroll labeled READ ME.</title>
<style>
#a {
padding: 0 0 0 64px;
color: blue;
margin: 0;
}
</style>
</head>
<body>
<div id=a>blah blah blah</div>
</body>
</html>

View file

@ -0,0 +1,33 @@
<!DOCTYPE html>
<html>
<head>
<title>You see here a scroll labeled VE FORBRYDERNE.</title>
<style>
div {
width: 50px;
}
#a {
background: red;
height: 50px;
min-height: 100px;
}
#b {
background: green;
height: 100px;
max-height: 50px;
}
#c {
background: blue;
height: 50px;
min-height: 100px; /* <-- this one overrides per the spec */
max-height: 25px;
}
</style>
</head>
<body>
<div id=a></div>
<div id=b></div>
<div id=c></div>
</body>
</html>

View file

@ -0,0 +1,29 @@
<!DOCTYPE html>
<html>
<head>
<title>You see here a scroll labeled VE FORBRYDERNE.</title>
<style>
div {
width: 50px;
}
#a {
background: red;
height: 100px;
}
#b {
background: green;
height: 50px;
}
#c {
background: blue;
height: 100px;
}
</style>
</head>
<body>
<div id=a></div>
<div id=b></div>
<div id=c></div>
</body>
</html>

View file

@ -0,0 +1,17 @@
<!DOCTYPE html>
<html>
<head>
<style>
#a {
line-height: 100px;
}
#b {
line-height: 6px;
}
</style>
<body>
<div id=a><span id=b>Four score and seven years ago our fathers brought forth on this continent, a new nation, conceived in Liberty, and dedicated to the proposition that all men are created equal. Now we are engaged in a great civil war, testing whether that nation, or any nation so conceived and so dedicated, can long endure. We are met on a great battle-field of that war. We have come to dedicate a portion of that field, as a final resting place for those who here gave their lives that that nation might live. It is altogether fitting and proper that we should do this.</span></div>
</body>
</html>

View file

@ -0,0 +1,17 @@
<!DOCTYPE html>
<html>
<head>
<style>
#a {
line-height: 100px;
}
#b {
line-height: 100px;
}
</style>
<body>
<div id=a><span id=b>Four score and seven years ago our fathers brought forth on this continent, a new nation, conceived in Liberty, and dedicated to the proposition that all men are created equal. Now we are engaged in a great civil war, testing whether that nation, or any nation so conceived and so dedicated, can long endure. We are met on a great battle-field of that war. We have come to dedicate a portion of that field, as a final resting place for those who here gave their lives that that nation might live. It is altogether fitting and proper that we should do this.</span></div>
</body>
</html>

View file

@ -0,0 +1,31 @@
<!DOCTYPE html>
<html>
<head>
<title>You see here a scroll labeled KERNOD WEL.</title>
<style>
body {
font-size: 40px;
}
#d {
float: right;
background: green;
color: white;
}
#b {
margin: 0 0 -100px 0;
}
#c {
margin: 100px 0 0 0;
clear: both;
background-color: blue;
color: white;
}
</style>
</head>
<body>
<div id=d>Beetlejuice</div>
<div id=a><div id=b>Beetlejuice</div></div>
<div id=c>Beetlejuice</div>
</body>
</html>

View file

@ -0,0 +1,27 @@
<!DOCTYPE html>
<html>
<head>
<title>You see here a scroll labeled KERNOD WEL.</title>
<style>
body {
font-size: 40px;
}
#d {
float: right;
background: green;
color: white;
}
#c {
clear: both;
background-color: blue;
color: white;
}
</style>
</head>
<body>
<div id=d>Beetlejuice</div>
<div id=a>Beetlejuice</div>
<div id=c>Beetlejuice</div>
</body>
</html>

View file

@ -0,0 +1,19 @@
<!DOCTYPE html>
<html>
<head>
<title>You see here a scroll labeled JUYED AWK YACC.</title>
<style>
* {
line-height: 14px;
}
#b {
margin-top: -14px;
}
</style>
</head>
<body>
<div id=a>Here lies the body of Jonathan Blake.</div>
<div id=b>Stepped on the gas instead of the brake.</div>
</body>
</html>

View file

@ -0,0 +1,25 @@
<!DOCTYPE html>
<html>
<head>
<title>You see here a scroll labeled JUYED AWK YACC.</title>
<style>
* {
line-height: 14px;
}
#a {
position: relative;
}
#b {
position: absolute;
top: 0;
left: 0;
right: 0;
}
</style>
</head>
<body>
<div id=a>Here lies the body of Jonathan Blake.
<div id=b>Stepped on the gas instead of the brake.</div></div>
</body>
</html>