mirror of
https://github.com/servo/servo.git
synced 2025-07-25 16:20:36 +01:00
layout: Largely move display list building out to a separate file.
`layout::fragment` and `layout::block` were getting too big.
This commit is contained in:
parent
691e42f7ef
commit
821793351e
13 changed files with 843 additions and 785 deletions
|
@ -29,6 +29,7 @@
|
|||
|
||||
use construct::FlowConstructor;
|
||||
use context::LayoutContext;
|
||||
use display_list_builder::{BlockFlowDisplayListBuilding, FragmentDisplayListBuilding};
|
||||
use floats::{ClearBoth, ClearLeft, ClearRight, FloatKind, FloatLeft, Floats, PlacementInfo};
|
||||
use flow::{BaseFlow, BlockFlowClass, FlowClass, Flow, ImmutableFlowUtils};
|
||||
use flow::{MutableFlowUtils, PreorderFlowTraversal, PostorderFlowTraversal, mut_base};
|
||||
|
@ -40,24 +41,18 @@ use model::{MaybeAuto, NoCollapsibleMargins, Specified, specified, specified_or_
|
|||
use table::ColumnInlineSize;
|
||||
use wrapper::ThreadSafeLayoutNode;
|
||||
|
||||
use collections::dlist::DList;
|
||||
use geom::{Size2D, Point2D, Rect};
|
||||
use gfx::color;
|
||||
use gfx::display_list::{BackgroundAndBorderLevel, BlockLevel, ContentStackingLevel, DisplayList};
|
||||
use gfx::display_list::{FloatStackingLevel, PositionedDescendantStackingLevel};
|
||||
use gfx::display_list::{RootOfStackingContextLevel};
|
||||
use gfx::render_task::RenderLayer;
|
||||
use geom::Size2D;
|
||||
use gfx::display_list::BlockLevel;
|
||||
use serialize::{Encoder, Encodable};
|
||||
use servo_msg::compositor_msg::{FixedPosition, LayerId, Scrollable};
|
||||
use servo_msg::compositor_msg::LayerId;
|
||||
use servo_util::geometry::{Au, MAX_AU, MAX_RECT};
|
||||
use servo_util::logical_geometry::{LogicalPoint, LogicalRect, LogicalSize};
|
||||
use servo_util::opts;
|
||||
use std::cmp::{max, min};
|
||||
use std::fmt;
|
||||
use std::mem;
|
||||
use style::computed_values::{LPA_Auto, LPA_Length, LPA_Percentage, LPN_Length, LPN_None};
|
||||
use style::computed_values::{LPN_Percentage, LP_Length, LP_Percentage, box_sizing, clear};
|
||||
use style::computed_values::{display, float, overflow, position};
|
||||
use sync::Arc;
|
||||
|
||||
/// Information specific to floated blocks.
|
||||
#[deriving(Clone, Encodable)]
|
||||
|
@ -1070,70 +1065,6 @@ impl BlockFlow {
|
|||
self.base.position = self.base.position.translate(&float_offset).translate(&margin_offset);
|
||||
}
|
||||
|
||||
fn build_display_list_block_common(&mut self,
|
||||
layout_context: &LayoutContext,
|
||||
background_border_level: BackgroundAndBorderLevel) {
|
||||
let relative_offset =
|
||||
self.fragment.relative_position(&self.base
|
||||
.absolute_position_info
|
||||
.relative_containing_block_size);
|
||||
|
||||
// Add the box that starts the block context.
|
||||
let mut display_list = DisplayList::new();
|
||||
self.fragment.build_display_list(&mut display_list,
|
||||
layout_context,
|
||||
self.base.abs_position.add_size(
|
||||
&relative_offset.to_physical(self.base.writing_mode)),
|
||||
background_border_level,
|
||||
&self.base.clip_rect);
|
||||
|
||||
let mut child_layers = DList::new();
|
||||
for kid in self.base.child_iter() {
|
||||
if kid.is_absolutely_positioned() {
|
||||
// All absolute flows will be handled by their containing block.
|
||||
continue
|
||||
}
|
||||
|
||||
display_list.push_all_move(mem::replace(&mut flow::mut_base(kid).display_list,
|
||||
DisplayList::new()));
|
||||
child_layers.append(mem::replace(&mut flow::mut_base(kid).layers, DList::new()))
|
||||
}
|
||||
|
||||
// Process absolute descendant links.
|
||||
for abs_descendant_link in self.base.abs_descendants.iter() {
|
||||
// TODO(pradeep): Send in our absolute position directly.
|
||||
display_list.push_all_move(mem::replace(
|
||||
&mut flow::mut_base(abs_descendant_link).display_list,
|
||||
DisplayList::new()));
|
||||
child_layers.append(mem::replace(&mut flow::mut_base(abs_descendant_link).layers,
|
||||
DList::new()));
|
||||
}
|
||||
|
||||
self.base.display_list = display_list;
|
||||
self.base.layers = child_layers
|
||||
}
|
||||
|
||||
/// Add display items for current block.
|
||||
///
|
||||
/// Set the absolute position for children after doing any offsetting for
|
||||
/// position: relative.
|
||||
pub fn build_display_list_block(&mut self, layout_context: &LayoutContext) {
|
||||
if self.is_float() {
|
||||
// TODO(#2009, pcwalton): This is a pseudo-stacking context. We need to merge `z-index:
|
||||
// auto` kids into the parent stacking context, when that is supported.
|
||||
self.build_display_list_float(layout_context)
|
||||
} else if self.is_absolutely_positioned() {
|
||||
self.build_display_list_abs(layout_context)
|
||||
} else {
|
||||
self.build_display_list_block_common(layout_context, BlockLevel)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_display_list_float(&mut self, layout_context: &LayoutContext) {
|
||||
self.build_display_list_block_common(layout_context, RootOfStackingContextLevel);
|
||||
self.base.display_list = mem::replace(&mut self.base.display_list,
|
||||
DisplayList::new()).flatten(FloatStackingLevel)
|
||||
}
|
||||
|
||||
/// Calculate and set the block-size, offsets, etc. for absolutely positioned flow.
|
||||
///
|
||||
|
@ -1232,43 +1163,6 @@ impl BlockFlow {
|
|||
self.base.position.size.block = block_size;
|
||||
}
|
||||
|
||||
/// Add display items for Absolutely Positioned flow.
|
||||
fn build_display_list_abs(&mut self, layout_context: &LayoutContext) {
|
||||
self.build_display_list_block_common(layout_context, RootOfStackingContextLevel);
|
||||
|
||||
if !self.base.absolute_position_info.layers_needed_for_positioned_flows &&
|
||||
!self.base.flags.needs_layer() {
|
||||
// We didn't need a layer.
|
||||
let z_index = self.fragment.style().get_box().z_index.number_or_zero();
|
||||
let level = PositionedDescendantStackingLevel(z_index);
|
||||
self.base.display_list = mem::replace(&mut self.base.display_list,
|
||||
DisplayList::new()).flatten(level);
|
||||
return
|
||||
}
|
||||
|
||||
// If we got here, then we need a new layer.
|
||||
let layer_rect = self.base.position.union(&self.base.overflow);
|
||||
let size = Size2D(layer_rect.size.inline.to_nearest_px() as uint,
|
||||
layer_rect.size.block.to_nearest_px() as uint);
|
||||
let origin = Point2D(self.base.abs_position.x.to_nearest_px() as uint,
|
||||
self.base.abs_position.y.to_nearest_px() as uint);
|
||||
|
||||
let scroll_policy = if self.is_fixed() {
|
||||
FixedPosition
|
||||
} else {
|
||||
Scrollable
|
||||
};
|
||||
let display_list = mem::replace(&mut self.base.display_list, DisplayList::new());
|
||||
let new_layer = RenderLayer {
|
||||
id: self.layer_id(0),
|
||||
display_list: Arc::new(display_list.flatten(ContentStackingLevel)),
|
||||
position: Rect(origin, size),
|
||||
background_color: color::rgba(1.0, 1.0, 1.0, 0.0),
|
||||
scroll_policy: scroll_policy,
|
||||
};
|
||||
self.base.layers.push(new_layer)
|
||||
}
|
||||
|
||||
/// Return the block-start outer edge of the hypothetical box for an absolute flow.
|
||||
///
|
||||
/// This is wrt its parent flow box.
|
||||
|
@ -1839,6 +1733,22 @@ impl Flow for BlockFlow {
|
|||
self.base.position.start.b = block_position
|
||||
}
|
||||
}
|
||||
|
||||
fn build_display_list(&mut self, layout_context: &LayoutContext) {
|
||||
if self.is_float() {
|
||||
// TODO(#2009, pcwalton): This is a pseudo-stacking context. We need to merge `z-index:
|
||||
// auto` kids into the parent stacking context, when that is supported.
|
||||
self.build_display_list_for_floating_block(layout_context)
|
||||
} else if self.is_absolutely_positioned() {
|
||||
self.build_display_list_for_absolutely_positioned_block(layout_context)
|
||||
} else {
|
||||
self.build_display_list_for_block(layout_context, BlockLevel)
|
||||
}
|
||||
|
||||
if opts::get().validate_display_list_geometry {
|
||||
self.base.validate_display_list_geometry();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Show for BlockFlow {
|
||||
|
@ -2562,3 +2472,4 @@ fn propagate_column_inline_sizes_to_child(kid: &mut Flow,
|
|||
*inline_start_margin_edge = *inline_start_margin_edge + inline_size
|
||||
}
|
||||
}
|
||||
|
||||
|
|
710
components/layout/display_list_builder.rs
Normal file
710
components/layout/display_list_builder.rs
Normal file
|
@ -0,0 +1,710 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* 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/. */
|
||||
|
||||
//! Builds display lists from flows and fragments.
|
||||
//!
|
||||
//! Other browser engines sometimes call this "painting", but it is more accurately called display
|
||||
//! list building, as the actual painting does not happen here—only deciding *what* we're going to
|
||||
//! paint.
|
||||
|
||||
#![deny(unsafe_block)]
|
||||
|
||||
use block::BlockFlow;
|
||||
use context::LayoutContext;
|
||||
use flow::{mod, Flow};
|
||||
use fragment::{Fragment, GenericFragment, IframeFragment, IframeFragmentInfo, ImageFragment};
|
||||
use fragment::{ImageFragmentInfo, InlineAbsoluteHypotheticalFragment, InlineBlockFragment};
|
||||
use fragment::{InputFragment, ScannedTextFragment, ScannedTextFragmentInfo, TableFragment};
|
||||
use fragment::{TableCellFragment, TableColumnFragment, TableRowFragment, TableWrapperFragment};
|
||||
use fragment::{UnscannedTextFragment};
|
||||
use model;
|
||||
use util::{OpaqueNodeMethods, ToGfxColor};
|
||||
|
||||
use collections::dlist::DList;
|
||||
use geom::approxeq::ApproxEq;
|
||||
use geom::{Point2D, Rect, Size2D, SideOffsets2D};
|
||||
use gfx::color;
|
||||
use gfx::display_list::{BackgroundAndBorderLevel, BaseDisplayItem, BorderDisplayItem};
|
||||
use gfx::display_list::{BorderDisplayItemClass, ContentStackingLevel, DisplayList};
|
||||
use gfx::display_list::{FloatStackingLevel, ImageDisplayItem, ImageDisplayItemClass};
|
||||
use gfx::display_list::{LineDisplayItem, LineDisplayItemClass, PositionedDescendantStackingLevel};
|
||||
use gfx::display_list::{PseudoDisplayItemClass, RootOfStackingContextLevel, SidewaysLeft};
|
||||
use gfx::display_list::{SidewaysRight, SolidColorDisplayItem, SolidColorDisplayItemClass};
|
||||
use gfx::display_list::{StackingLevel, TextDisplayItem, TextDisplayItemClass, Upright};
|
||||
use gfx::render_task::RenderLayer;
|
||||
use servo_msg::compositor_msg::{FixedPosition, Scrollable};
|
||||
use servo_msg::constellation_msg::{ConstellationChan, FrameRectMsg};
|
||||
use servo_net::image::holder::ImageHolder;
|
||||
use servo_util::geometry::{mod, Au, ZERO_RECT};
|
||||
use servo_util::logical_geometry::{LogicalRect, WritingMode};
|
||||
use servo_util::opts;
|
||||
use std::mem;
|
||||
use style::{ComputedValues, RGBA};
|
||||
use style::computed_values::{background_attachment, background_repeat, border_style, overflow};
|
||||
use style::computed_values::{visibility};
|
||||
use sync::Arc;
|
||||
|
||||
pub trait FragmentDisplayListBuilding {
|
||||
/// Adds the display items necessary to paint the background of this fragment to the display
|
||||
/// list if necessary.
|
||||
fn build_display_list_for_background_if_applicable(&self,
|
||||
style: &ComputedValues,
|
||||
list: &mut DisplayList,
|
||||
layout_context: &LayoutContext,
|
||||
level: StackingLevel,
|
||||
absolute_bounds: &Rect<Au>,
|
||||
clip_rect: &Rect<Au>);
|
||||
|
||||
/// Adds the display items necessary to paint the borders of this fragment to a display list if
|
||||
/// necessary.
|
||||
fn build_display_list_for_borders_if_applicable(&self,
|
||||
style: &ComputedValues,
|
||||
list: &mut DisplayList,
|
||||
abs_bounds: &Rect<Au>,
|
||||
level: StackingLevel,
|
||||
clip_rect: &Rect<Au>);
|
||||
|
||||
fn build_debug_borders_around_text_fragments(&self,
|
||||
display_list: &mut DisplayList,
|
||||
flow_origin: Point2D<Au>,
|
||||
text_fragment: &ScannedTextFragmentInfo,
|
||||
clip_rect: &Rect<Au>);
|
||||
|
||||
fn build_debug_borders_around_fragment(&self,
|
||||
display_list: &mut DisplayList,
|
||||
flow_origin: Point2D<Au>,
|
||||
clip_rect: &Rect<Au>);
|
||||
|
||||
/// Adds the display items for this fragment to the given stacking context.
|
||||
///
|
||||
/// Arguments:
|
||||
///
|
||||
/// * `display_list`: The unflattened display list to add display items to.
|
||||
/// * `layout_context`: The layout context.
|
||||
/// * `dirty`: The dirty rectangle in the coordinate system of the owning flow.
|
||||
/// * `flow_origin`: Position of the origin of the owning flow wrt the display list root flow.
|
||||
/// * `clip_rect`: The rectangle to clip the display items to.
|
||||
fn build_display_list(&mut self,
|
||||
display_list: &mut DisplayList,
|
||||
layout_context: &LayoutContext,
|
||||
flow_origin: Point2D<Au>,
|
||||
background_and_border_level: BackgroundAndBorderLevel,
|
||||
clip_rect: &Rect<Au>);
|
||||
|
||||
/// Sends the size and position of this iframe fragment to the constellation. This is out of
|
||||
/// line to guide inlining.
|
||||
fn finalize_position_and_size_of_iframe(&self,
|
||||
iframe_fragment: &IframeFragmentInfo,
|
||||
offset: Point2D<Au>,
|
||||
layout_context: &LayoutContext);
|
||||
|
||||
fn clip_rect_for_children(&self, current_clip_rect: Rect<Au>, flow_origin: Point2D<Au>)
|
||||
-> Rect<Au>;
|
||||
}
|
||||
|
||||
impl FragmentDisplayListBuilding for Fragment {
|
||||
fn build_display_list_for_background_if_applicable(&self,
|
||||
style: &ComputedValues,
|
||||
list: &mut DisplayList,
|
||||
layout_context: &LayoutContext,
|
||||
level: StackingLevel,
|
||||
absolute_bounds: &Rect<Au>,
|
||||
clip_rect: &Rect<Au>) {
|
||||
// FIXME: This causes a lot of background colors to be displayed when they are clearly not
|
||||
// needed. We could use display list optimization to clean this up, but it still seems
|
||||
// inefficient. What we really want is something like "nearest ancestor element that
|
||||
// doesn't have a fragment".
|
||||
let background_color = style.resolve_color(style.get_background().background_color);
|
||||
if !background_color.alpha.approx_eq(&0.0) {
|
||||
let display_item = box SolidColorDisplayItem {
|
||||
base: BaseDisplayItem::new(*absolute_bounds, self.node, level, *clip_rect),
|
||||
color: background_color.to_gfx_color(),
|
||||
};
|
||||
|
||||
list.push(SolidColorDisplayItemClass(display_item))
|
||||
}
|
||||
|
||||
// The background image is painted on top of the background color.
|
||||
// Implements background image, per spec:
|
||||
// http://www.w3.org/TR/CSS21/colors.html#background
|
||||
let background = style.get_background();
|
||||
let image_url = match background.background_image {
|
||||
None => return,
|
||||
Some(ref image_url) => image_url,
|
||||
};
|
||||
|
||||
let mut holder = ImageHolder::new(image_url.clone(),
|
||||
layout_context.shared.image_cache.clone());
|
||||
let image = match holder.get_image(self.node.to_untrusted_node_address()) {
|
||||
None => {
|
||||
// No image data at all? Do nothing.
|
||||
//
|
||||
// TODO: Add some kind of placeholder background image.
|
||||
debug!("(building display list) no background image :(");
|
||||
return
|
||||
}
|
||||
Some(image) => image,
|
||||
};
|
||||
debug!("(building display list) building background image");
|
||||
|
||||
let image_width = Au::from_px(image.width as int);
|
||||
let image_height = Au::from_px(image.height as int);
|
||||
let mut bounds = *absolute_bounds;
|
||||
|
||||
// Clip.
|
||||
//
|
||||
// TODO: Check the bounds to see if a clip item is actually required.
|
||||
let clip_rect = clip_rect.intersection(&bounds).unwrap_or(ZERO_RECT);
|
||||
|
||||
// Use background-attachment to get the initial virtual origin
|
||||
let (virtual_origin_x, virtual_origin_y) = match background.background_attachment {
|
||||
background_attachment::scroll => {
|
||||
(absolute_bounds.origin.x, absolute_bounds.origin.y)
|
||||
}
|
||||
background_attachment::fixed => {
|
||||
(Au(0), Au(0))
|
||||
}
|
||||
};
|
||||
|
||||
// Use background-position to get the offset
|
||||
let horizontal_position = model::specified(background.background_position.horizontal,
|
||||
bounds.size.width - image_width);
|
||||
let vertical_position = model::specified(background.background_position.vertical,
|
||||
bounds.size.height - image_height);
|
||||
|
||||
let abs_x = virtual_origin_x + horizontal_position;
|
||||
let abs_y = virtual_origin_y + vertical_position;
|
||||
|
||||
// Adjust origin and size based on background-repeat
|
||||
match background.background_repeat {
|
||||
background_repeat::no_repeat => {
|
||||
bounds.origin.x = abs_x;
|
||||
bounds.origin.y = abs_y;
|
||||
bounds.size.width = image_width;
|
||||
bounds.size.height = image_height;
|
||||
}
|
||||
background_repeat::repeat_x => {
|
||||
bounds.origin.y = abs_y;
|
||||
bounds.size.height = image_height;
|
||||
ImageFragmentInfo::tile_image(&mut bounds.origin.x, &mut bounds.size.width,
|
||||
abs_x, image.width);
|
||||
}
|
||||
background_repeat::repeat_y => {
|
||||
bounds.origin.x = abs_x;
|
||||
bounds.size.width = image_width;
|
||||
ImageFragmentInfo::tile_image(&mut bounds.origin.y, &mut bounds.size.height,
|
||||
abs_y, image.height);
|
||||
}
|
||||
background_repeat::repeat => {
|
||||
ImageFragmentInfo::tile_image(&mut bounds.origin.x, &mut bounds.size.width,
|
||||
abs_x, image.width);
|
||||
ImageFragmentInfo::tile_image(&mut bounds.origin.y, &mut bounds.size.height,
|
||||
abs_y, image.height);
|
||||
}
|
||||
};
|
||||
|
||||
// Create the image display item.
|
||||
let image_display_item = ImageDisplayItemClass(box ImageDisplayItem {
|
||||
base: BaseDisplayItem::new(bounds, self.node, level, clip_rect),
|
||||
image: image.clone(),
|
||||
stretch_size: Size2D(Au::from_px(image.width as int),
|
||||
Au::from_px(image.height as int)),
|
||||
});
|
||||
list.push(image_display_item)
|
||||
}
|
||||
|
||||
/// Adds the display items necessary to paint the borders of this fragment to a display list if
|
||||
/// necessary.
|
||||
fn build_display_list_for_borders_if_applicable(&self,
|
||||
style: &ComputedValues,
|
||||
list: &mut DisplayList,
|
||||
abs_bounds: &Rect<Au>,
|
||||
level: StackingLevel,
|
||||
clip_rect: &Rect<Au>) {
|
||||
let border = style.logical_border_width();
|
||||
if border.is_zero() {
|
||||
return
|
||||
}
|
||||
|
||||
let top_color = style.resolve_color(style.get_border().border_top_color);
|
||||
let right_color = style.resolve_color(style.get_border().border_right_color);
|
||||
let bottom_color = style.resolve_color(style.get_border().border_bottom_color);
|
||||
let left_color = style.resolve_color(style.get_border().border_left_color);
|
||||
|
||||
// Append the border to the display list.
|
||||
let border_display_item = box BorderDisplayItem {
|
||||
base: BaseDisplayItem::new(*abs_bounds, self.node, level, *clip_rect),
|
||||
border: border.to_physical(style.writing_mode),
|
||||
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(style.get_border().border_top_style,
|
||||
style.get_border().border_right_style,
|
||||
style.get_border().border_bottom_style,
|
||||
style.get_border().border_left_style)
|
||||
};
|
||||
|
||||
list.push(BorderDisplayItemClass(border_display_item))
|
||||
}
|
||||
|
||||
fn build_debug_borders_around_text_fragments(&self,
|
||||
display_list: &mut DisplayList,
|
||||
flow_origin: Point2D<Au>,
|
||||
text_fragment: &ScannedTextFragmentInfo,
|
||||
clip_rect: &Rect<Au>) {
|
||||
// FIXME(#2795): Get the real container size
|
||||
let container_size = Size2D::zero();
|
||||
// Fragment position wrt to the owning flow.
|
||||
let fragment_bounds = self.border_box.to_physical(self.style.writing_mode, container_size);
|
||||
let absolute_fragment_bounds = Rect(
|
||||
fragment_bounds.origin + flow_origin,
|
||||
fragment_bounds.size);
|
||||
|
||||
// Compute the text fragment bounds and draw a border surrounding them.
|
||||
let border_display_item = box BorderDisplayItem {
|
||||
base: BaseDisplayItem::new(absolute_fragment_bounds,
|
||||
self.node,
|
||||
ContentStackingLevel,
|
||||
*clip_rect),
|
||||
border: SideOffsets2D::new_all_same(Au::from_px(1)),
|
||||
color: SideOffsets2D::new_all_same(color::rgb(0, 0, 200)),
|
||||
style: SideOffsets2D::new_all_same(border_style::solid)
|
||||
};
|
||||
display_list.push(BorderDisplayItemClass(border_display_item));
|
||||
|
||||
// Draw a rectangle representing the baselines.
|
||||
let ascent = text_fragment.run.ascent();
|
||||
let mut baseline = self.border_box.clone();
|
||||
baseline.start.b = baseline.start.b + ascent;
|
||||
baseline.size.block = Au(0);
|
||||
let mut baseline = baseline.to_physical(self.style.writing_mode, container_size);
|
||||
baseline.origin = baseline.origin + flow_origin;
|
||||
|
||||
let line_display_item = box LineDisplayItem {
|
||||
base: BaseDisplayItem::new(baseline, self.node, ContentStackingLevel, *clip_rect),
|
||||
color: color::rgb(0, 200, 0),
|
||||
style: border_style::dashed,
|
||||
};
|
||||
display_list.push(LineDisplayItemClass(line_display_item));
|
||||
}
|
||||
|
||||
fn build_debug_borders_around_fragment(&self,
|
||||
display_list: &mut DisplayList,
|
||||
flow_origin: Point2D<Au>,
|
||||
clip_rect: &Rect<Au>) {
|
||||
// FIXME(#2795): Get the real container size
|
||||
let container_size = Size2D::zero();
|
||||
// Fragment position wrt to the owning flow.
|
||||
let fragment_bounds = self.border_box.to_physical(self.style.writing_mode, container_size);
|
||||
let absolute_fragment_bounds = Rect(
|
||||
fragment_bounds.origin + flow_origin,
|
||||
fragment_bounds.size);
|
||||
|
||||
// This prints a debug border around the border of this fragment.
|
||||
let border_display_item = box BorderDisplayItem {
|
||||
base: BaseDisplayItem::new(absolute_fragment_bounds,
|
||||
self.node,
|
||||
ContentStackingLevel,
|
||||
*clip_rect),
|
||||
border: SideOffsets2D::new_all_same(Au::from_px(1)),
|
||||
color: SideOffsets2D::new_all_same(color::rgb(0, 0, 200)),
|
||||
style: SideOffsets2D::new_all_same(border_style::solid)
|
||||
};
|
||||
display_list.push(BorderDisplayItemClass(border_display_item))
|
||||
}
|
||||
|
||||
fn build_display_list(&mut self,
|
||||
display_list: &mut DisplayList,
|
||||
layout_context: &LayoutContext,
|
||||
flow_origin: Point2D<Au>,
|
||||
background_and_border_level: BackgroundAndBorderLevel,
|
||||
clip_rect: &Rect<Au>) {
|
||||
// FIXME(#2795): Get the real container size
|
||||
let container_size = Size2D::zero();
|
||||
let rect_to_absolute = |writing_mode: WritingMode, logical_rect: LogicalRect<Au>| {
|
||||
let physical_rect = logical_rect.to_physical(writing_mode, container_size);
|
||||
Rect(physical_rect.origin + flow_origin, physical_rect.size)
|
||||
};
|
||||
// Fragment position wrt to the owning flow.
|
||||
let absolute_fragment_bounds = rect_to_absolute(self.style.writing_mode, self.border_box);
|
||||
debug!("Fragment::build_display_list at rel={}, abs={}: {}",
|
||||
self.border_box,
|
||||
absolute_fragment_bounds,
|
||||
self);
|
||||
debug!("Fragment::build_display_list: dirty={}, flow_origin={}",
|
||||
layout_context.shared.dirty,
|
||||
flow_origin);
|
||||
|
||||
if self.style().get_inheritedbox().visibility != visibility::visible {
|
||||
return
|
||||
}
|
||||
|
||||
if !absolute_fragment_bounds.intersects(&layout_context.shared.dirty) {
|
||||
debug!("Fragment::build_display_list: Did not intersect...");
|
||||
return
|
||||
}
|
||||
|
||||
debug!("Fragment::build_display_list: intersected. Adding display item...");
|
||||
|
||||
if self.is_primary_fragment() {
|
||||
let level =
|
||||
StackingLevel::from_background_and_border_level(background_and_border_level);
|
||||
|
||||
// Add a pseudo-display item for content box queries. This is a very bogus thing to do.
|
||||
let base_display_item = box BaseDisplayItem::new(absolute_fragment_bounds,
|
||||
self.node,
|
||||
level,
|
||||
*clip_rect);
|
||||
display_list.push(PseudoDisplayItemClass(base_display_item));
|
||||
|
||||
// Add the background to the list, if applicable.
|
||||
match self.inline_context {
|
||||
Some(ref inline_context) => {
|
||||
for style in inline_context.styles.iter().rev() {
|
||||
self.build_display_list_for_background_if_applicable(
|
||||
&**style,
|
||||
display_list,
|
||||
layout_context,
|
||||
level,
|
||||
&absolute_fragment_bounds,
|
||||
clip_rect);
|
||||
}
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
match self.specific {
|
||||
ScannedTextFragment(_) => {},
|
||||
_ => {
|
||||
self.build_display_list_for_background_if_applicable(
|
||||
&*self.style,
|
||||
display_list,
|
||||
layout_context,
|
||||
level,
|
||||
&absolute_fragment_bounds,
|
||||
clip_rect);
|
||||
}
|
||||
}
|
||||
|
||||
// Add a border, if applicable.
|
||||
//
|
||||
// TODO: Outlines.
|
||||
match self.inline_context {
|
||||
Some(ref inline_context) => {
|
||||
for style in inline_context.styles.iter().rev() {
|
||||
self.build_display_list_for_borders_if_applicable(
|
||||
&**style,
|
||||
display_list,
|
||||
&absolute_fragment_bounds,
|
||||
level,
|
||||
clip_rect);
|
||||
}
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
match self.specific {
|
||||
ScannedTextFragment(_) => {},
|
||||
_ => {
|
||||
self.build_display_list_for_borders_if_applicable(
|
||||
&*self.style,
|
||||
display_list,
|
||||
&absolute_fragment_bounds,
|
||||
level,
|
||||
clip_rect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let content_box = self.content_box();
|
||||
let absolute_content_box = rect_to_absolute(self.style.writing_mode, content_box);
|
||||
|
||||
// Create special per-fragment-type display items.
|
||||
match self.specific {
|
||||
UnscannedTextFragment(_) => fail!("Shouldn't see unscanned fragments here."),
|
||||
TableColumnFragment(_) => fail!("Shouldn't see table column fragments here."),
|
||||
ScannedTextFragment(ref text_fragment) => {
|
||||
// Create the text display item.
|
||||
let orientation = if self.style.writing_mode.is_vertical() {
|
||||
if self.style.writing_mode.is_sideways_left() {
|
||||
SidewaysLeft
|
||||
} else {
|
||||
SidewaysRight
|
||||
}
|
||||
} else {
|
||||
Upright
|
||||
};
|
||||
|
||||
let metrics = &text_fragment.run.font_metrics;
|
||||
let baseline_origin ={
|
||||
let mut tmp = content_box.start;
|
||||
tmp.b = tmp.b + metrics.ascent;
|
||||
tmp.to_physical(self.style.writing_mode, container_size) + flow_origin
|
||||
};
|
||||
|
||||
let text_display_item = box TextDisplayItem {
|
||||
base: BaseDisplayItem::new(absolute_content_box,
|
||||
self.node,
|
||||
ContentStackingLevel,
|
||||
*clip_rect),
|
||||
text_run: text_fragment.run.clone(),
|
||||
range: text_fragment.range,
|
||||
text_color: self.style().get_color().color.to_gfx_color(),
|
||||
orientation: orientation,
|
||||
baseline_origin: baseline_origin,
|
||||
};
|
||||
display_list.push(TextDisplayItemClass(text_display_item));
|
||||
|
||||
// Create display items for text decoration
|
||||
{
|
||||
let line = |maybe_color: Option<RGBA>, rect: || -> LogicalRect<Au>| {
|
||||
match maybe_color {
|
||||
None => {},
|
||||
Some(color) => {
|
||||
display_list.push(SolidColorDisplayItemClass(
|
||||
box SolidColorDisplayItem {
|
||||
base: BaseDisplayItem::new(
|
||||
rect_to_absolute(
|
||||
self.style.writing_mode,
|
||||
rect()),
|
||||
self.node,
|
||||
ContentStackingLevel,
|
||||
*clip_rect),
|
||||
color: color.to_gfx_color(),
|
||||
}));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let text_decorations =
|
||||
self.style().get_inheritedtext()._servo_text_decorations_in_effect;
|
||||
line(text_decorations.underline, || {
|
||||
let mut rect = content_box.clone();
|
||||
rect.start.b = rect.start.b + metrics.ascent - metrics.underline_offset;
|
||||
rect.size.block = metrics.underline_size;
|
||||
rect
|
||||
});
|
||||
|
||||
line(text_decorations.overline, || {
|
||||
let mut rect = content_box.clone();
|
||||
rect.size.block = metrics.underline_size;
|
||||
rect
|
||||
});
|
||||
|
||||
line(text_decorations.line_through, || {
|
||||
let mut rect = content_box.clone();
|
||||
rect.start.b = rect.start.b + metrics.ascent - metrics.strikeout_offset;
|
||||
rect.size.block = metrics.strikeout_size;
|
||||
rect
|
||||
});
|
||||
}
|
||||
|
||||
if opts::get().show_debug_fragment_borders {
|
||||
self.build_debug_borders_around_text_fragments(display_list,
|
||||
flow_origin,
|
||||
text_fragment,
|
||||
clip_rect);
|
||||
}
|
||||
}
|
||||
GenericFragment | IframeFragment(..) | TableFragment | TableCellFragment |
|
||||
TableRowFragment | TableWrapperFragment | InlineBlockFragment(_) | InputFragment |
|
||||
InlineAbsoluteHypotheticalFragment(_) => {
|
||||
if opts::get().show_debug_fragment_borders {
|
||||
self.build_debug_borders_around_fragment(display_list,
|
||||
flow_origin,
|
||||
clip_rect);
|
||||
}
|
||||
}
|
||||
ImageFragment(ref mut image_fragment) => {
|
||||
let image_ref = &mut image_fragment.image;
|
||||
match image_ref.get_image(self.node.to_untrusted_node_address()) {
|
||||
Some(image) => {
|
||||
debug!("(building display list) building image fragment");
|
||||
|
||||
// Place the image into the display list.
|
||||
let image_display_item = box ImageDisplayItem {
|
||||
base: BaseDisplayItem::new(absolute_content_box,
|
||||
self.node,
|
||||
ContentStackingLevel,
|
||||
*clip_rect),
|
||||
image: image.clone(),
|
||||
stretch_size: absolute_content_box.size,
|
||||
};
|
||||
|
||||
display_list.push(ImageDisplayItemClass(image_display_item))
|
||||
}
|
||||
None => {
|
||||
// No image data at all? Do nothing.
|
||||
//
|
||||
// TODO: Add some kind of placeholder image.
|
||||
debug!("(building display list) no image :(");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if opts::get().show_debug_fragment_borders {
|
||||
self.build_debug_borders_around_fragment(display_list, flow_origin, clip_rect)
|
||||
}
|
||||
|
||||
// If this is an iframe, then send its position and size up to the constellation.
|
||||
//
|
||||
// FIXME(pcwalton): Doing this during display list construction seems potentially
|
||||
// problematic if iframes are outside the area we're computing the display list for, since
|
||||
// they won't be able to reflow at all until the user scrolls to them. Perhaps we should
|
||||
// separate this into two parts: first we should send the size only to the constellation
|
||||
// once that's computed during assign-block-sizes, and second we should should send the
|
||||
// origin to the constellation here during display list construction. This should work
|
||||
// because layout for the iframe only needs to know size, and origin is only relevant if
|
||||
// the iframe is actually going to be displayed.
|
||||
match self.specific {
|
||||
IframeFragment(ref iframe_fragment) => {
|
||||
self.finalize_position_and_size_of_iframe(iframe_fragment,
|
||||
absolute_fragment_bounds.origin,
|
||||
layout_context)
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
fn finalize_position_and_size_of_iframe(&self,
|
||||
iframe_fragment: &IframeFragmentInfo,
|
||||
offset: Point2D<Au>,
|
||||
layout_context: &LayoutContext) {
|
||||
let border_padding = (self.border_padding).to_physical(self.style.writing_mode);
|
||||
let content_size = self.content_box().size.to_physical(self.style.writing_mode);
|
||||
let iframe_rect = Rect(Point2D(geometry::to_frac_px(offset.x + border_padding.left) as f32,
|
||||
geometry::to_frac_px(offset.y + border_padding.top) as f32),
|
||||
Size2D(geometry::to_frac_px(content_size.width) as f32,
|
||||
geometry::to_frac_px(content_size.height) as f32));
|
||||
|
||||
debug!("finalizing position and size of iframe for {:?},{:?}",
|
||||
iframe_fragment.pipeline_id,
|
||||
iframe_fragment.subpage_id);
|
||||
let ConstellationChan(ref chan) = layout_context.shared.constellation_chan;
|
||||
chan.send(FrameRectMsg(iframe_fragment.pipeline_id,
|
||||
iframe_fragment.subpage_id,
|
||||
iframe_rect));
|
||||
}
|
||||
|
||||
fn clip_rect_for_children(&self, current_clip_rect: Rect<Au>, flow_origin: Point2D<Au>)
|
||||
-> Rect<Au> {
|
||||
// Don't clip if we're text.
|
||||
match self.specific {
|
||||
ScannedTextFragment(_) => return current_clip_rect,
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// Only clip if `overflow` tells us to.
|
||||
match self.style.get_box().overflow {
|
||||
overflow::hidden | overflow::auto | overflow::scroll => {}
|
||||
_ => return current_clip_rect,
|
||||
}
|
||||
|
||||
// Create a new clip rect.
|
||||
//
|
||||
// FIXME(#2795): Get the real container size.
|
||||
let physical_rect = self.border_box.to_physical(self.style.writing_mode, Size2D::zero());
|
||||
current_clip_rect.intersection(&Rect(physical_rect.origin + flow_origin,
|
||||
physical_rect.size)).unwrap_or(ZERO_RECT)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait BlockFlowDisplayListBuilding {
|
||||
fn build_display_list_for_block(&mut self,
|
||||
layout_context: &LayoutContext,
|
||||
background_border_level: BackgroundAndBorderLevel);
|
||||
fn build_display_list_for_absolutely_positioned_block(&mut self,
|
||||
layout_context: &LayoutContext);
|
||||
fn build_display_list_for_floating_block(&mut self, layout_context: &LayoutContext);
|
||||
}
|
||||
|
||||
impl BlockFlowDisplayListBuilding for BlockFlow {
|
||||
fn build_display_list_for_block(&mut self,
|
||||
layout_context: &LayoutContext,
|
||||
background_border_level: BackgroundAndBorderLevel) {
|
||||
let relative_offset =
|
||||
self.fragment.relative_position(&self.base
|
||||
.absolute_position_info
|
||||
.relative_containing_block_size);
|
||||
|
||||
// Add the box that starts the block context.
|
||||
let mut display_list = DisplayList::new();
|
||||
self.fragment.build_display_list(&mut display_list,
|
||||
layout_context,
|
||||
self.base.abs_position.add_size(
|
||||
&relative_offset.to_physical(self.base.writing_mode)),
|
||||
background_border_level,
|
||||
&self.base.clip_rect);
|
||||
|
||||
let mut child_layers = DList::new();
|
||||
for kid in self.base.child_iter() {
|
||||
if kid.is_absolutely_positioned() {
|
||||
// All absolute flows will be handled by their containing block.
|
||||
continue
|
||||
}
|
||||
|
||||
display_list.push_all_move(mem::replace(&mut flow::mut_base(kid).display_list,
|
||||
DisplayList::new()));
|
||||
child_layers.append(mem::replace(&mut flow::mut_base(kid).layers, DList::new()))
|
||||
}
|
||||
|
||||
// Process absolute descendant links.
|
||||
for abs_descendant_link in self.base.abs_descendants.iter() {
|
||||
// TODO(pradeep): Send in our absolute position directly.
|
||||
display_list.push_all_move(mem::replace(
|
||||
&mut flow::mut_base(abs_descendant_link).display_list,
|
||||
DisplayList::new()));
|
||||
child_layers.append(mem::replace(&mut flow::mut_base(abs_descendant_link).layers,
|
||||
DList::new()));
|
||||
}
|
||||
|
||||
self.base.display_list = display_list;
|
||||
self.base.layers = child_layers
|
||||
}
|
||||
|
||||
fn build_display_list_for_absolutely_positioned_block(&mut self,
|
||||
layout_context: &LayoutContext) {
|
||||
self.build_display_list_for_block(layout_context, RootOfStackingContextLevel);
|
||||
|
||||
if !self.base.absolute_position_info.layers_needed_for_positioned_flows &&
|
||||
!self.base.flags.needs_layer() {
|
||||
// We didn't need a layer.
|
||||
let z_index = self.fragment.style().get_box().z_index.number_or_zero();
|
||||
let level = PositionedDescendantStackingLevel(z_index);
|
||||
self.base.display_list = mem::replace(&mut self.base.display_list,
|
||||
DisplayList::new()).flatten(level);
|
||||
return
|
||||
}
|
||||
|
||||
// If we got here, then we need a new layer.
|
||||
let layer_rect = self.base.position.union(&self.base.overflow);
|
||||
let size = Size2D(layer_rect.size.inline.to_nearest_px() as uint,
|
||||
layer_rect.size.block.to_nearest_px() as uint);
|
||||
let origin = Point2D(self.base.abs_position.x.to_nearest_px() as uint,
|
||||
self.base.abs_position.y.to_nearest_px() as uint);
|
||||
|
||||
let scroll_policy = if self.is_fixed() {
|
||||
FixedPosition
|
||||
} else {
|
||||
Scrollable
|
||||
};
|
||||
let display_list = mem::replace(&mut self.base.display_list, DisplayList::new());
|
||||
let new_layer = RenderLayer {
|
||||
id: self.layer_id(0),
|
||||
display_list: Arc::new(display_list.flatten(ContentStackingLevel)),
|
||||
position: Rect(origin, size),
|
||||
background_color: color::rgba(1.0, 1.0, 1.0, 0.0),
|
||||
scroll_policy: scroll_policy,
|
||||
};
|
||||
self.base.layers.push(new_layer)
|
||||
}
|
||||
|
||||
fn build_display_list_for_floating_block(&mut self, layout_context: &LayoutContext) {
|
||||
self.build_display_list_for_block(layout_context, RootOfStackingContextLevel);
|
||||
self.base.display_list = mem::replace(&mut self.base.display_list,
|
||||
DisplayList::new()).flatten(FloatStackingLevel)
|
||||
}
|
||||
}
|
||||
|
|
@ -54,7 +54,6 @@ use servo_msg::compositor_msg::LayerId;
|
|||
use servo_util::geometry::Au;
|
||||
use servo_util::logical_geometry::WritingMode;
|
||||
use servo_util::logical_geometry::{LogicalRect, LogicalSize};
|
||||
use servo_util::opts;
|
||||
use std::mem;
|
||||
use std::num::Zero;
|
||||
use std::fmt;
|
||||
|
@ -209,6 +208,9 @@ pub trait Flow: fmt::Show + ToString + Sync {
|
|||
// The default implementation is a no-op.
|
||||
}
|
||||
|
||||
/// Phase 5 of reflow: builds display lists.
|
||||
fn build_display_list(&mut self, layout_context: &LayoutContext);
|
||||
|
||||
/// Returns the direction that this flow clears floats in, if any.
|
||||
fn float_clearance(&self) -> clear::T {
|
||||
clear::none
|
||||
|
@ -406,9 +408,6 @@ pub trait ImmutableFlowUtils {
|
|||
|
||||
/// Dumps the flow tree for debugging, with a prefix to indicate that we're at the given level.
|
||||
fn dump_with_level(self, level: uint);
|
||||
|
||||
/// Print an error when this Flow's display list items are not within its boundaries.
|
||||
fn validate_display_list_geometry(self);
|
||||
}
|
||||
|
||||
pub trait MutableFlowUtils {
|
||||
|
@ -425,9 +424,6 @@ pub trait MutableFlowUtils {
|
|||
/// Computes the overflow region for this flow.
|
||||
fn store_overflow(self, _: &LayoutContext);
|
||||
|
||||
/// Builds the display lists for this flow.
|
||||
fn build_display_list(self, layout_context: &LayoutContext);
|
||||
|
||||
/// Gathers static block-offsets bubbled up by kids.
|
||||
///
|
||||
/// This essentially gives us offsets of all absolutely positioned direct descendants and all
|
||||
|
@ -858,6 +854,28 @@ impl BaseFlow {
|
|||
let p = self as *const _;
|
||||
p as uint
|
||||
}
|
||||
|
||||
pub fn validate_display_list_geometry(&self) {
|
||||
let position_with_overflow = self.position.union(&self.overflow);
|
||||
let bounds = Rect(self.abs_position,
|
||||
Size2D(position_with_overflow.size.inline,
|
||||
position_with_overflow.size.block));
|
||||
|
||||
for item in self.display_list.iter() {
|
||||
let paint_bounds = match item.base().bounds.intersection(&item.base().clip_rect) {
|
||||
None => continue,
|
||||
Some(rect) => rect,
|
||||
};
|
||||
|
||||
if paint_bounds.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
if bounds.union(&paint_bounds) != bounds {
|
||||
error!("DisplayList item {} outside of Flow overflow ({})", item, paint_bounds);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ImmutableFlowUtils for &'a Flow + 'a {
|
||||
|
@ -1028,28 +1046,6 @@ impl<'a> ImmutableFlowUtils for &'a Flow + 'a {
|
|||
kid.dump_with_level(level + 1)
|
||||
}
|
||||
}
|
||||
|
||||
fn validate_display_list_geometry(self) {
|
||||
let position_with_overflow = base(self).position.union(&base(self).overflow);
|
||||
let bounds = Rect(base(self).abs_position,
|
||||
Size2D(position_with_overflow.size.inline,
|
||||
position_with_overflow.size.block));
|
||||
|
||||
for item in base(self).display_list.iter() {
|
||||
let paint_bounds = match item.base().bounds.intersection(&item.base().clip_rect) {
|
||||
None => continue,
|
||||
Some(rect) => rect,
|
||||
};
|
||||
|
||||
if paint_bounds.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
if bounds.union(&paint_bounds) != bounds {
|
||||
error!("DisplayList item {} outside of Flow overflow ({})", item, paint_bounds);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> MutableFlowUtils for &'a mut Flow + 'a {
|
||||
|
@ -1110,44 +1106,6 @@ impl<'a> MutableFlowUtils for &'a mut Flow + 'a {
|
|||
mut_base(self).overflow = overflow;
|
||||
}
|
||||
|
||||
/// Push display items for current flow and its descendants onto the appropriate display lists
|
||||
/// of the given stacking context.
|
||||
///
|
||||
/// Arguments:
|
||||
///
|
||||
/// * `builder`: The display list builder, which contains information used during the entire
|
||||
/// display list building pass.
|
||||
///
|
||||
/// * `info`: Per-flow display list building information.
|
||||
fn build_display_list(self, layout_context: &LayoutContext) {
|
||||
debug!("Flow: building display list");
|
||||
match self.class() {
|
||||
BlockFlowClass => self.as_block().build_display_list_block(layout_context),
|
||||
InlineFlowClass => self.as_inline().build_display_list_inline(layout_context),
|
||||
TableWrapperFlowClass => {
|
||||
self.as_table_wrapper().build_display_list_table_wrapper(layout_context)
|
||||
}
|
||||
TableFlowClass => self.as_table().build_display_list_table(layout_context),
|
||||
TableRowGroupFlowClass => {
|
||||
self.as_table_rowgroup().build_display_list_table_rowgroup(layout_context)
|
||||
}
|
||||
TableRowFlowClass => self.as_table_row().build_display_list_table_row(layout_context),
|
||||
TableCaptionFlowClass => {
|
||||
self.as_table_caption().build_display_list_table_caption(layout_context)
|
||||
}
|
||||
TableCellFlowClass => {
|
||||
self.as_table_cell().build_display_list_table_cell(layout_context)
|
||||
}
|
||||
TableColGroupFlowClass => {
|
||||
// Nothing to do here, as column groups don't render.
|
||||
}
|
||||
}
|
||||
|
||||
if opts::get().validate_display_list_geometry {
|
||||
self.validate_display_list_geometry();
|
||||
}
|
||||
}
|
||||
|
||||
/// Collect and update static y-offsets bubbled up by kids.
|
||||
///
|
||||
/// This would essentially give us offsets of all absolutely positioned
|
||||
|
|
|
@ -19,44 +19,33 @@ use layout_debug;
|
|||
use model::{Auto, IntrinsicISizes, IntrinsicISizesContribution, MaybeAuto, Specified, specified};
|
||||
use model;
|
||||
use text;
|
||||
use util::{OpaqueNodeMethods, ToGfxColor};
|
||||
use util::OpaqueNodeMethods;
|
||||
use wrapper::{TLayoutNode, ThreadSafeLayoutNode};
|
||||
|
||||
use geom::{Point2D, Rect, Size2D, SideOffsets2D};
|
||||
use geom::approxeq::ApproxEq;
|
||||
use gfx::color::rgb;
|
||||
use gfx::display_list::{BackgroundAndBorderLevel, BaseDisplayItem, BorderDisplayItem};
|
||||
use gfx::display_list::{BorderDisplayItemClass, ContentStackingLevel, DisplayList};
|
||||
use gfx::display_list::{ImageDisplayItem, ImageDisplayItemClass, LineDisplayItem};
|
||||
use gfx::display_list::{LineDisplayItemClass, OpaqueNode, PseudoDisplayItemClass};
|
||||
use gfx::display_list::{SidewaysLeft, SidewaysRight, SolidColorDisplayItem};
|
||||
use gfx::display_list::{SolidColorDisplayItemClass, StackingLevel, TextDisplayItem};
|
||||
use gfx::display_list::{TextDisplayItemClass, Upright};
|
||||
use geom::Size2D;
|
||||
use gfx::display_list::OpaqueNode;
|
||||
use gfx::text::glyph::CharIndex;
|
||||
use gfx::text::text_run::TextRun;
|
||||
use script_traits::UntrustedNodeAddress;
|
||||
use serialize::{Encodable, Encoder};
|
||||
use servo_msg::constellation_msg::{ConstellationChan, FrameRectMsg, PipelineId, SubpageId};
|
||||
use servo_msg::constellation_msg::{PipelineId, SubpageId};
|
||||
use servo_net::image::holder::ImageHolder;
|
||||
use servo_net::local_image_cache::LocalImageCache;
|
||||
use servo_util::geometry::{Au, ZERO_RECT};
|
||||
use servo_util::geometry::Au;
|
||||
use servo_util::geometry;
|
||||
use servo_util::logical_geometry::{LogicalRect, LogicalSize, LogicalMargin, WritingMode};
|
||||
use servo_util::opts;
|
||||
use servo_util::logical_geometry::{LogicalRect, LogicalSize, LogicalMargin};
|
||||
use servo_util::range::*;
|
||||
use servo_util::smallvec::SmallVec;
|
||||
use servo_util::str::is_whitespace;
|
||||
use std::cmp::{max, min};
|
||||
use std::fmt;
|
||||
use std::from_str::FromStr;
|
||||
use std::num::Zero;
|
||||
use string_cache::Atom;
|
||||
use style::{ComputedValues, TElement, TNode, cascade_anonymous, RGBA};
|
||||
use style::{ComputedValues, TElement, TNode, cascade_anonymous};
|
||||
use style::computed_values::{LengthOrPercentage, LengthOrPercentageOrAuto};
|
||||
use style::computed_values::{LengthOrPercentageOrNone};
|
||||
use style::computed_values::{overflow, LPA_Auto, background_attachment};
|
||||
use style::computed_values::{background_repeat, border_style, clear, position, text_align};
|
||||
use style::computed_values::{text_decoration, vertical_align, visibility, white_space};
|
||||
use style::computed_values::{LPA_Auto, clear, position, text_align, text_decoration};
|
||||
use style::computed_values::{vertical_align, white_space};
|
||||
use sync::{Arc, Mutex};
|
||||
use url::Url;
|
||||
|
||||
|
@ -132,6 +121,8 @@ impl<E, S: Encoder<E>> Encodable<S, E> for Fragment {
|
|||
}
|
||||
|
||||
/// Info specific to the kind of fragment. Keep this enum small.
|
||||
///
|
||||
/// FIXME(pcwalton): We have completely failed at keeping this enum small.
|
||||
#[deriving(Clone)]
|
||||
pub enum SpecificFragmentInfo {
|
||||
GenericFragment,
|
||||
|
@ -935,480 +926,6 @@ impl Fragment {
|
|||
}
|
||||
}
|
||||
|
||||
/// Adds the display items necessary to paint the background of this fragment to the display
|
||||
/// list if necessary.
|
||||
pub fn build_display_list_for_background_if_applicable(&self,
|
||||
style: &ComputedValues,
|
||||
list: &mut DisplayList,
|
||||
layout_context: &LayoutContext,
|
||||
level: StackingLevel,
|
||||
absolute_bounds: &Rect<Au>,
|
||||
clip_rect: &Rect<Au>) {
|
||||
// FIXME: This causes a lot of background colors to be displayed when they are clearly not
|
||||
// needed. We could use display list optimization to clean this up, but it still seems
|
||||
// inefficient. What we really want is something like "nearest ancestor element that
|
||||
// doesn't have a fragment".
|
||||
let background_color = style.resolve_color(style.get_background().background_color);
|
||||
if !background_color.alpha.approx_eq(&0.0) {
|
||||
let display_item = box SolidColorDisplayItem {
|
||||
base: BaseDisplayItem::new(*absolute_bounds, self.node, level, *clip_rect),
|
||||
color: background_color.to_gfx_color(),
|
||||
};
|
||||
|
||||
list.push(SolidColorDisplayItemClass(display_item))
|
||||
}
|
||||
|
||||
// The background image is painted on top of the background color.
|
||||
// Implements background image, per spec:
|
||||
// http://www.w3.org/TR/CSS21/colors.html#background
|
||||
let background = style.get_background();
|
||||
let image_url = match background.background_image {
|
||||
None => return,
|
||||
Some(ref image_url) => image_url,
|
||||
};
|
||||
|
||||
let mut holder = ImageHolder::new(image_url.clone(), layout_context.shared.image_cache.clone());
|
||||
let image = match holder.get_image(self.node.to_untrusted_node_address()) {
|
||||
None => {
|
||||
// No image data at all? Do nothing.
|
||||
//
|
||||
// TODO: Add some kind of placeholder background image.
|
||||
debug!("(building display list) no background image :(");
|
||||
return
|
||||
}
|
||||
Some(image) => image,
|
||||
};
|
||||
debug!("(building display list) building background image");
|
||||
|
||||
let image_width = Au::from_px(image.width as int);
|
||||
let image_height = Au::from_px(image.height as int);
|
||||
let mut bounds = *absolute_bounds;
|
||||
|
||||
// Clip.
|
||||
//
|
||||
// TODO: Check the bounds to see if a clip item is actually required.
|
||||
let clip_rect = clip_rect.intersection(&bounds).unwrap_or(ZERO_RECT);
|
||||
|
||||
// Use background-attachment to get the initial virtual origin
|
||||
let (virtual_origin_x, virtual_origin_y) = match background.background_attachment {
|
||||
background_attachment::scroll => {
|
||||
(absolute_bounds.origin.x, absolute_bounds.origin.y)
|
||||
}
|
||||
background_attachment::fixed => {
|
||||
(Au(0), Au(0))
|
||||
}
|
||||
};
|
||||
|
||||
// Use background-position to get the offset
|
||||
let horizontal_position = model::specified(background.background_position.horizontal,
|
||||
bounds.size.width - image_width);
|
||||
let vertical_position = model::specified(background.background_position.vertical,
|
||||
bounds.size.height - image_height);
|
||||
|
||||
let abs_x = virtual_origin_x + horizontal_position;
|
||||
let abs_y = virtual_origin_y + vertical_position;
|
||||
|
||||
// Adjust origin and size based on background-repeat
|
||||
match background.background_repeat {
|
||||
background_repeat::no_repeat => {
|
||||
bounds.origin.x = abs_x;
|
||||
bounds.origin.y = abs_y;
|
||||
bounds.size.width = image_width;
|
||||
bounds.size.height = image_height;
|
||||
}
|
||||
background_repeat::repeat_x => {
|
||||
bounds.origin.y = abs_y;
|
||||
bounds.size.height = image_height;
|
||||
ImageFragmentInfo::tile_image(&mut bounds.origin.x, &mut bounds.size.width,
|
||||
abs_x, image.width);
|
||||
}
|
||||
background_repeat::repeat_y => {
|
||||
bounds.origin.x = abs_x;
|
||||
bounds.size.width = image_width;
|
||||
ImageFragmentInfo::tile_image(&mut bounds.origin.y, &mut bounds.size.height,
|
||||
abs_y, image.height);
|
||||
}
|
||||
background_repeat::repeat => {
|
||||
ImageFragmentInfo::tile_image(&mut bounds.origin.x, &mut bounds.size.width,
|
||||
abs_x, image.width);
|
||||
ImageFragmentInfo::tile_image(&mut bounds.origin.y, &mut bounds.size.height,
|
||||
abs_y, image.height);
|
||||
}
|
||||
};
|
||||
|
||||
// Create the image display item.
|
||||
let image_display_item = ImageDisplayItemClass(box ImageDisplayItem {
|
||||
base: BaseDisplayItem::new(bounds, self.node, level, clip_rect),
|
||||
image: image.clone(),
|
||||
stretch_size: Size2D(Au::from_px(image.width as int),
|
||||
Au::from_px(image.height as int)),
|
||||
});
|
||||
list.push(image_display_item)
|
||||
}
|
||||
|
||||
/// Adds the display items necessary to paint the borders of this fragment to a display list if
|
||||
/// necessary.
|
||||
pub fn build_display_list_for_borders_if_applicable(&self,
|
||||
style: &ComputedValues,
|
||||
list: &mut DisplayList,
|
||||
abs_bounds: &Rect<Au>,
|
||||
level: StackingLevel,
|
||||
clip_rect: &Rect<Au>) {
|
||||
let border = style.logical_border_width();
|
||||
if border.is_zero() {
|
||||
return
|
||||
}
|
||||
|
||||
let top_color = style.resolve_color(style.get_border().border_top_color);
|
||||
let right_color = style.resolve_color(style.get_border().border_right_color);
|
||||
let bottom_color = style.resolve_color(style.get_border().border_bottom_color);
|
||||
let left_color = style.resolve_color(style.get_border().border_left_color);
|
||||
|
||||
// Append the border to the display list.
|
||||
let border_display_item = box BorderDisplayItem {
|
||||
base: BaseDisplayItem::new(*abs_bounds, self.node, level, *clip_rect),
|
||||
border: border.to_physical(style.writing_mode),
|
||||
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(style.get_border().border_top_style,
|
||||
style.get_border().border_right_style,
|
||||
style.get_border().border_bottom_style,
|
||||
style.get_border().border_left_style)
|
||||
};
|
||||
|
||||
list.push(BorderDisplayItemClass(border_display_item))
|
||||
}
|
||||
|
||||
fn build_debug_borders_around_text_fragments(&self,
|
||||
display_list: &mut DisplayList,
|
||||
flow_origin: Point2D<Au>,
|
||||
text_fragment: &ScannedTextFragmentInfo,
|
||||
clip_rect: &Rect<Au>) {
|
||||
// FIXME(#2795): Get the real container size
|
||||
let container_size = Size2D::zero();
|
||||
// Fragment position wrt to the owning flow.
|
||||
let fragment_bounds = self.border_box.to_physical(self.style.writing_mode, container_size);
|
||||
let absolute_fragment_bounds = Rect(
|
||||
fragment_bounds.origin + flow_origin,
|
||||
fragment_bounds.size);
|
||||
|
||||
// Compute the text fragment bounds and draw a border surrounding them.
|
||||
let border_display_item = box BorderDisplayItem {
|
||||
base: BaseDisplayItem::new(absolute_fragment_bounds,
|
||||
self.node,
|
||||
ContentStackingLevel,
|
||||
*clip_rect),
|
||||
border: SideOffsets2D::new_all_same(Au::from_px(1)),
|
||||
color: SideOffsets2D::new_all_same(rgb(0, 0, 200)),
|
||||
style: SideOffsets2D::new_all_same(border_style::solid)
|
||||
};
|
||||
display_list.push(BorderDisplayItemClass(border_display_item));
|
||||
|
||||
// Draw a rectangle representing the baselines.
|
||||
let ascent = text_fragment.run.ascent();
|
||||
let mut baseline = self.border_box.clone();
|
||||
baseline.start.b = baseline.start.b + ascent;
|
||||
baseline.size.block = Au(0);
|
||||
let mut baseline = baseline.to_physical(self.style.writing_mode, container_size);
|
||||
baseline.origin = baseline.origin + flow_origin;
|
||||
|
||||
let line_display_item = box LineDisplayItem {
|
||||
base: BaseDisplayItem::new(baseline, self.node, ContentStackingLevel, *clip_rect),
|
||||
color: rgb(0, 200, 0),
|
||||
style: border_style::dashed,
|
||||
};
|
||||
display_list.push(LineDisplayItemClass(line_display_item));
|
||||
}
|
||||
|
||||
fn build_debug_borders_around_fragment(&self,
|
||||
display_list: &mut DisplayList,
|
||||
flow_origin: Point2D<Au>,
|
||||
clip_rect: &Rect<Au>) {
|
||||
// FIXME(#2795): Get the real container size
|
||||
let container_size = Size2D::zero();
|
||||
// Fragment position wrt to the owning flow.
|
||||
let fragment_bounds = self.border_box.to_physical(self.style.writing_mode, container_size);
|
||||
let absolute_fragment_bounds = Rect(
|
||||
fragment_bounds.origin + flow_origin,
|
||||
fragment_bounds.size);
|
||||
|
||||
// This prints a debug border around the border of this fragment.
|
||||
let border_display_item = box BorderDisplayItem {
|
||||
base: BaseDisplayItem::new(absolute_fragment_bounds,
|
||||
self.node,
|
||||
ContentStackingLevel,
|
||||
*clip_rect),
|
||||
border: SideOffsets2D::new_all_same(Au::from_px(1)),
|
||||
color: SideOffsets2D::new_all_same(rgb(0, 0, 200)),
|
||||
style: SideOffsets2D::new_all_same(border_style::solid)
|
||||
};
|
||||
display_list.push(BorderDisplayItemClass(border_display_item))
|
||||
}
|
||||
|
||||
/// Adds the display items for this fragment to the given stacking context.
|
||||
///
|
||||
/// Arguments:
|
||||
///
|
||||
/// * `display_list`: The unflattened display list to add display items to.
|
||||
/// * `layout_context`: The layout context.
|
||||
/// * `dirty`: The dirty rectangle in the coordinate system of the owning flow.
|
||||
/// * `flow_origin`: Position of the origin of the owning flow wrt the display list root flow.
|
||||
/// * `clip_rect`: The rectangle to clip the display items to.
|
||||
pub fn build_display_list(&mut self,
|
||||
display_list: &mut DisplayList,
|
||||
layout_context: &LayoutContext,
|
||||
flow_origin: Point2D<Au>,
|
||||
background_and_border_level: BackgroundAndBorderLevel,
|
||||
clip_rect: &Rect<Au>) {
|
||||
// FIXME(#2795): Get the real container size
|
||||
let container_size = Size2D::zero();
|
||||
let rect_to_absolute = |writing_mode: WritingMode, logical_rect: LogicalRect<Au>| {
|
||||
let physical_rect = logical_rect.to_physical(writing_mode, container_size);
|
||||
Rect(physical_rect.origin + flow_origin, physical_rect.size)
|
||||
};
|
||||
// Fragment position wrt to the owning flow.
|
||||
let absolute_fragment_bounds = rect_to_absolute(self.style.writing_mode, self.border_box);
|
||||
debug!("Fragment::build_display_list at rel={}, abs={}: {}",
|
||||
self.border_box,
|
||||
absolute_fragment_bounds,
|
||||
self);
|
||||
debug!("Fragment::build_display_list: dirty={}, flow_origin={}",
|
||||
layout_context.shared.dirty,
|
||||
flow_origin);
|
||||
|
||||
if self.style().get_inheritedbox().visibility != visibility::visible {
|
||||
return
|
||||
}
|
||||
|
||||
if !absolute_fragment_bounds.intersects(&layout_context.shared.dirty) {
|
||||
debug!("Fragment::build_display_list: Did not intersect...");
|
||||
return
|
||||
}
|
||||
|
||||
debug!("Fragment::build_display_list: intersected. Adding display item...");
|
||||
|
||||
if self.is_primary_fragment() {
|
||||
let level =
|
||||
StackingLevel::from_background_and_border_level(background_and_border_level);
|
||||
|
||||
// Add a pseudo-display item for content box queries. This is a very bogus thing to do.
|
||||
let base_display_item = box BaseDisplayItem::new(absolute_fragment_bounds,
|
||||
self.node,
|
||||
level,
|
||||
*clip_rect);
|
||||
display_list.push(PseudoDisplayItemClass(base_display_item));
|
||||
|
||||
// Add the background to the list, if applicable.
|
||||
match self.inline_context {
|
||||
Some(ref inline_context) => {
|
||||
for style in inline_context.styles.iter().rev() {
|
||||
self.build_display_list_for_background_if_applicable(
|
||||
&**style,
|
||||
display_list,
|
||||
layout_context,
|
||||
level,
|
||||
&absolute_fragment_bounds,
|
||||
clip_rect);
|
||||
}
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
match self.specific {
|
||||
ScannedTextFragment(_) => {},
|
||||
_ => {
|
||||
self.build_display_list_for_background_if_applicable(
|
||||
&*self.style,
|
||||
display_list,
|
||||
layout_context,
|
||||
level,
|
||||
&absolute_fragment_bounds,
|
||||
clip_rect);
|
||||
}
|
||||
}
|
||||
|
||||
// Add a border, if applicable.
|
||||
//
|
||||
// TODO: Outlines.
|
||||
match self.inline_context {
|
||||
Some(ref inline_context) => {
|
||||
for style in inline_context.styles.iter().rev() {
|
||||
self.build_display_list_for_borders_if_applicable(
|
||||
&**style,
|
||||
display_list,
|
||||
&absolute_fragment_bounds,
|
||||
level,
|
||||
clip_rect);
|
||||
}
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
match self.specific {
|
||||
ScannedTextFragment(_) => {},
|
||||
_ => {
|
||||
self.build_display_list_for_borders_if_applicable(
|
||||
&*self.style,
|
||||
display_list,
|
||||
&absolute_fragment_bounds,
|
||||
level,
|
||||
clip_rect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let content_box = self.content_box();
|
||||
let absolute_content_box = rect_to_absolute(self.style.writing_mode, content_box);
|
||||
|
||||
// Create special per-fragment-type display items.
|
||||
match self.specific {
|
||||
UnscannedTextFragment(_) => fail!("Shouldn't see unscanned fragments here."),
|
||||
TableColumnFragment(_) => fail!("Shouldn't see table column fragments here."),
|
||||
ScannedTextFragment(ref text_fragment) => {
|
||||
// Create the text display item.
|
||||
let orientation = if self.style.writing_mode.is_vertical() {
|
||||
if self.style.writing_mode.is_sideways_left() {
|
||||
SidewaysLeft
|
||||
} else {
|
||||
SidewaysRight
|
||||
}
|
||||
} else {
|
||||
Upright
|
||||
};
|
||||
|
||||
let metrics = &text_fragment.run.font_metrics;
|
||||
let baseline_origin ={
|
||||
let mut tmp = content_box.start;
|
||||
tmp.b = tmp.b + metrics.ascent;
|
||||
tmp.to_physical(self.style.writing_mode, container_size) + flow_origin
|
||||
};
|
||||
|
||||
let text_display_item = box TextDisplayItem {
|
||||
base: BaseDisplayItem::new(absolute_content_box,
|
||||
self.node,
|
||||
ContentStackingLevel,
|
||||
*clip_rect),
|
||||
text_run: text_fragment.run.clone(),
|
||||
range: text_fragment.range,
|
||||
text_color: self.style().get_color().color.to_gfx_color(),
|
||||
orientation: orientation,
|
||||
baseline_origin: baseline_origin,
|
||||
};
|
||||
display_list.push(TextDisplayItemClass(text_display_item));
|
||||
|
||||
// Create display items for text decoration
|
||||
{
|
||||
let line = |maybe_color: Option<RGBA>, rect: || -> LogicalRect<Au>| {
|
||||
match maybe_color {
|
||||
None => {},
|
||||
Some(color) => {
|
||||
display_list.push(SolidColorDisplayItemClass(
|
||||
box SolidColorDisplayItem {
|
||||
base: BaseDisplayItem::new(
|
||||
rect_to_absolute(
|
||||
self.style.writing_mode,
|
||||
rect()),
|
||||
self.node,
|
||||
ContentStackingLevel,
|
||||
*clip_rect),
|
||||
color: color.to_gfx_color(),
|
||||
}));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let text_decorations =
|
||||
self.style().get_inheritedtext()._servo_text_decorations_in_effect;
|
||||
line(text_decorations.underline, || {
|
||||
let mut rect = content_box.clone();
|
||||
rect.start.b = rect.start.b + metrics.ascent - metrics.underline_offset;
|
||||
rect.size.block = metrics.underline_size;
|
||||
rect
|
||||
});
|
||||
|
||||
line(text_decorations.overline, || {
|
||||
let mut rect = content_box.clone();
|
||||
rect.size.block = metrics.underline_size;
|
||||
rect
|
||||
});
|
||||
|
||||
line(text_decorations.line_through, || {
|
||||
let mut rect = content_box.clone();
|
||||
rect.start.b = rect.start.b + metrics.ascent - metrics.strikeout_offset;
|
||||
rect.size.block = metrics.strikeout_size;
|
||||
rect
|
||||
});
|
||||
}
|
||||
|
||||
if opts::get().show_debug_fragment_borders {
|
||||
self.build_debug_borders_around_text_fragments(display_list,
|
||||
flow_origin,
|
||||
text_fragment,
|
||||
clip_rect);
|
||||
}
|
||||
}
|
||||
GenericFragment | IframeFragment(..) | TableFragment | TableCellFragment |
|
||||
TableRowFragment | TableWrapperFragment | InlineBlockFragment(_) | InputFragment |
|
||||
InlineAbsoluteHypotheticalFragment(_) => {
|
||||
if opts::get().show_debug_fragment_borders {
|
||||
self.build_debug_borders_around_fragment(display_list,
|
||||
flow_origin,
|
||||
clip_rect);
|
||||
}
|
||||
}
|
||||
ImageFragment(ref mut image_fragment) => {
|
||||
let image_ref = &mut image_fragment.image;
|
||||
match image_ref.get_image(self.node.to_untrusted_node_address()) {
|
||||
Some(image) => {
|
||||
debug!("(building display list) building image fragment");
|
||||
|
||||
// Place the image into the display list.
|
||||
let image_display_item = box ImageDisplayItem {
|
||||
base: BaseDisplayItem::new(absolute_content_box,
|
||||
self.node,
|
||||
ContentStackingLevel,
|
||||
*clip_rect),
|
||||
image: image.clone(),
|
||||
stretch_size: absolute_content_box.size,
|
||||
};
|
||||
|
||||
display_list.push(ImageDisplayItemClass(image_display_item))
|
||||
}
|
||||
None => {
|
||||
// No image data at all? Do nothing.
|
||||
//
|
||||
// TODO: Add some kind of placeholder image.
|
||||
debug!("(building display list) no image :(");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME(pcwalton): This is a bit of an abuse of the logging
|
||||
// infrastructure. We should have a real `SERVO_DEBUG` system.
|
||||
debug!("{:?}",
|
||||
self.build_debug_borders_around_fragment(display_list, flow_origin, clip_rect))
|
||||
|
||||
// If this is an iframe, then send its position and size up to the constellation.
|
||||
//
|
||||
// FIXME(pcwalton): Doing this during display list construction seems potentially
|
||||
// problematic if iframes are outside the area we're computing the display list for, since
|
||||
// they won't be able to reflow at all until the user scrolls to them. Perhaps we should
|
||||
// separate this into two parts: first we should send the size only to the constellation
|
||||
// once that's computed during assign-block-sizes, and second we should should send the
|
||||
// origin to the constellation here during display list construction. This should work
|
||||
// because layout for the iframe only needs to know size, and origin is only relevant if
|
||||
// the iframe is actually going to be displayed.
|
||||
match self.specific {
|
||||
IframeFragment(ref iframe_fragment) => {
|
||||
self.finalize_position_and_size_of_iframe(iframe_fragment,
|
||||
absolute_fragment_bounds.origin,
|
||||
layout_context)
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
/// Computes the intrinsic inline-sizes of this fragment.
|
||||
pub fn compute_intrinsic_inline_sizes(&mut self) -> IntrinsicISizesContribution {
|
||||
let mut result = self.style_specified_intrinsic_inline_size();
|
||||
|
@ -1889,29 +1406,6 @@ impl Fragment {
|
|||
}
|
||||
}
|
||||
|
||||
/// Sends the size and position of this iframe fragment to the constellation. This is out of
|
||||
/// line to guide inlining.
|
||||
#[inline(never)]
|
||||
fn finalize_position_and_size_of_iframe(&self,
|
||||
iframe_fragment: &IframeFragmentInfo,
|
||||
offset: Point2D<Au>,
|
||||
layout_context: &LayoutContext) {
|
||||
let border_padding = (self.border_padding).to_physical(self.style.writing_mode);
|
||||
let content_size = self.content_box().size.to_physical(self.style.writing_mode);
|
||||
let iframe_rect = Rect(Point2D(geometry::to_frac_px(offset.x + border_padding.left) as f32,
|
||||
geometry::to_frac_px(offset.y + border_padding.top) as f32),
|
||||
Size2D(geometry::to_frac_px(content_size.width) as f32,
|
||||
geometry::to_frac_px(content_size.height) as f32));
|
||||
|
||||
debug!("finalizing position and size of iframe for {:?},{:?}",
|
||||
iframe_fragment.pipeline_id,
|
||||
iframe_fragment.subpage_id);
|
||||
let ConstellationChan(ref chan) = layout_context.shared.constellation_chan;
|
||||
chan.send(FrameRectMsg(iframe_fragment.pipeline_id,
|
||||
iframe_fragment.subpage_id,
|
||||
iframe_rect));
|
||||
}
|
||||
|
||||
/// Returns true if and only if this is the *primary fragment* for the fragment's style object
|
||||
/// (conceptually, though style sharing makes this not really true, of course). The primary
|
||||
/// fragment is the one that draws backgrounds, borders, etc., and takes borders, padding and
|
||||
|
@ -1921,7 +1415,7 @@ impl Fragment {
|
|||
/// fragments. Inline-block fragments are not primary fragments because the corresponding block
|
||||
/// flow is the primary fragment, while table wrapper fragments are not primary fragments
|
||||
/// because the corresponding table flow is the primary fragment.
|
||||
fn is_primary_fragment(&self) -> bool {
|
||||
pub fn is_primary_fragment(&self) -> bool {
|
||||
match self.specific {
|
||||
InlineBlockFragment(_) | InlineAbsoluteHypotheticalFragment(_) |
|
||||
TableWrapperFragment => false,
|
||||
|
@ -1951,27 +1445,6 @@ impl Fragment {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn clip_rect_for_children(&self, current_clip_rect: Rect<Au>, flow_origin: Point2D<Au>)
|
||||
-> Rect<Au> {
|
||||
// Don't clip if we're text.
|
||||
match self.specific {
|
||||
ScannedTextFragment(_) => return current_clip_rect,
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// Only clip if `overflow` tells us to.
|
||||
match self.style.get_box().overflow {
|
||||
overflow::hidden | overflow::auto | overflow::scroll => {}
|
||||
_ => return current_clip_rect,
|
||||
}
|
||||
|
||||
// Create a new clip rect.
|
||||
//
|
||||
// FIXME(#2795): Get the real container size.
|
||||
let physical_rect = self.border_box.to_physical(self.style.writing_mode, Size2D::zero());
|
||||
current_clip_rect.intersection(&Rect(physical_rect.origin + flow_origin,
|
||||
physical_rect.size)).unwrap_or(ZERO_RECT)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Show for Fragment {
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
use css::node_style::StyledNode;
|
||||
use context::LayoutContext;
|
||||
use display_list_builder::FragmentDisplayListBuilding;
|
||||
use floats::{FloatLeft, Floats, PlacementInfo};
|
||||
use flow::{BaseFlow, FlowClass, Flow, InlineFlowClass, MutableFlowUtils};
|
||||
use flow;
|
||||
|
@ -24,6 +25,7 @@ use gfx::font_context::FontContext;
|
|||
use gfx::text::glyph::CharIndex;
|
||||
use servo_util::geometry::Au;
|
||||
use servo_util::logical_geometry::{LogicalRect, LogicalSize};
|
||||
use servo_util::opts;
|
||||
use servo_util::range::{IntRangeIndex, Range, RangeIndex};
|
||||
use servo_util::arc_ptr_eq;
|
||||
use std::cmp::max;
|
||||
|
@ -706,44 +708,6 @@ impl InlineFlow {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn build_display_list_inline(&mut self, layout_context: &LayoutContext) {
|
||||
let size = self.base.position.size.to_physical(self.base.writing_mode);
|
||||
if !Rect(self.base.abs_position, size).intersects(&layout_context.shared.dirty) {
|
||||
debug!("inline block (abs pos {}, size {}) didn't intersect \
|
||||
dirty rect two",
|
||||
self.base.abs_position,
|
||||
size);
|
||||
return
|
||||
}
|
||||
|
||||
// TODO(#228): Once we form lines and have their cached bounds, we can be smarter and
|
||||
// not recurse on a line if nothing in it can intersect the dirty region.
|
||||
debug!("Flow: building display list for {:u} inline fragments", self.fragments.len());
|
||||
|
||||
for fragment in self.fragments.fragments.iter_mut() {
|
||||
let rel_offset = fragment.relative_position(&self.base
|
||||
.absolute_position_info
|
||||
.relative_containing_block_size);
|
||||
let fragment_position = self.base
|
||||
.abs_position
|
||||
.add_size(&rel_offset.to_physical(self.base.writing_mode));
|
||||
fragment.build_display_list(&mut self.base.display_list,
|
||||
layout_context,
|
||||
fragment_position,
|
||||
ContentLevel,
|
||||
&self.base.clip_rect);
|
||||
match fragment.specific {
|
||||
InlineBlockFragment(ref mut block_flow) => {
|
||||
let block_flow = block_flow.flow_ref.deref_mut();
|
||||
self.base.display_list.push_all_move(
|
||||
mem::replace(&mut flow::mut_base(block_flow).display_list,
|
||||
DisplayList::new()));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the distance from the baseline for the logical block-start inline-start corner of
|
||||
/// this fragment, taking into account the value of the CSS `vertical-align` property.
|
||||
/// Negative values mean "toward the logical block-start" and positive values mean "toward the
|
||||
|
@ -1201,6 +1165,48 @@ impl Flow for InlineFlow {
|
|||
fn update_late_computed_inline_position_if_necessary(&mut self, _: Au) {}
|
||||
|
||||
fn update_late_computed_block_position_if_necessary(&mut self, _: Au) {}
|
||||
|
||||
fn build_display_list(&mut self, layout_context: &LayoutContext) {
|
||||
let size = self.base.position.size.to_physical(self.base.writing_mode);
|
||||
if !Rect(self.base.abs_position, size).intersects(&layout_context.shared.dirty) {
|
||||
debug!("inline block (abs pos {}, size {}) didn't intersect \
|
||||
dirty rect two",
|
||||
self.base.abs_position,
|
||||
size);
|
||||
return
|
||||
}
|
||||
|
||||
// TODO(#228): Once we form lines and have their cached bounds, we can be smarter and
|
||||
// not recurse on a line if nothing in it can intersect the dirty region.
|
||||
debug!("Flow: building display list for {:u} inline fragments", self.fragments.len());
|
||||
|
||||
for fragment in self.fragments.fragments.iter_mut() {
|
||||
let rel_offset = fragment.relative_position(&self.base
|
||||
.absolute_position_info
|
||||
.relative_containing_block_size);
|
||||
let fragment_position = self.base
|
||||
.abs_position
|
||||
.add_size(&rel_offset.to_physical(self.base.writing_mode));
|
||||
fragment.build_display_list(&mut self.base.display_list,
|
||||
layout_context,
|
||||
fragment_position,
|
||||
ContentLevel,
|
||||
&self.base.clip_rect);
|
||||
match fragment.specific {
|
||||
InlineBlockFragment(ref mut block_flow) => {
|
||||
let block_flow = block_flow.flow_ref.deref_mut();
|
||||
self.base.display_list.push_all_move(
|
||||
mem::replace(&mut flow::mut_base(block_flow).display_list,
|
||||
DisplayList::new()));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
if opts::get().validate_display_list_geometry {
|
||||
self.base.validate_display_list_geometry();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Show for InlineFlow {
|
||||
|
|
|
@ -46,6 +46,7 @@ pub mod layout_debug;
|
|||
pub mod block;
|
||||
pub mod construct;
|
||||
pub mod context;
|
||||
pub mod display_list_builder;
|
||||
pub mod floats;
|
||||
pub mod flow;
|
||||
pub mod flow_list;
|
||||
|
|
|
@ -126,11 +126,6 @@ impl TableFlow {
|
|||
fn assign_block_size_table_base<'a>(&mut self, layout_context: &'a LayoutContext<'a>) {
|
||||
self.block_flow.assign_block_size_block_base(layout_context, MarginsMayNotCollapse);
|
||||
}
|
||||
|
||||
pub fn build_display_list_table(&mut self, layout_context: &LayoutContext) {
|
||||
debug!("build_display_list_table: same process as block flow");
|
||||
self.block_flow.build_display_list_block(layout_context);
|
||||
}
|
||||
}
|
||||
|
||||
impl Flow for TableFlow {
|
||||
|
@ -323,6 +318,10 @@ impl Flow for TableFlow {
|
|||
fn update_late_computed_block_position_if_necessary(&mut self, block_position: Au) {
|
||||
self.block_flow.update_late_computed_block_position_if_necessary(block_position)
|
||||
}
|
||||
|
||||
fn build_display_list(&mut self, layout_context: &LayoutContext) {
|
||||
self.block_flow.build_display_list(layout_context);
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Show for TableFlow {
|
||||
|
|
|
@ -28,11 +28,6 @@ impl TableCaptionFlow {
|
|||
block_flow: BlockFlow::from_node(constructor, node)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_display_list_table_caption(&mut self, layout_context: &LayoutContext) {
|
||||
debug!("build_display_list_table_caption: same process as block flow");
|
||||
self.block_flow.build_display_list_block(layout_context)
|
||||
}
|
||||
}
|
||||
|
||||
impl Flow for TableCaptionFlow {
|
||||
|
@ -73,6 +68,11 @@ impl Flow for TableCaptionFlow {
|
|||
fn update_late_computed_block_position_if_necessary(&mut self, block_position: Au) {
|
||||
self.block_flow.update_late_computed_block_position_if_necessary(block_position)
|
||||
}
|
||||
|
||||
fn build_display_list(&mut self, layout_context: &LayoutContext) {
|
||||
debug!("build_display_list_table_caption: same process as block flow");
|
||||
self.block_flow.build_display_list(layout_context)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Show for TableCaptionFlow {
|
||||
|
|
|
@ -51,11 +51,6 @@ impl TableCellFlow {
|
|||
fn assign_block_size_table_cell_base<'a>(&mut self, layout_context: &'a LayoutContext<'a>) {
|
||||
self.block_flow.assign_block_size_block_base(layout_context, MarginsMayNotCollapse)
|
||||
}
|
||||
|
||||
pub fn build_display_list_table_cell(&mut self, layout_context: &LayoutContext) {
|
||||
debug!("build_display_list_table: same process as block flow");
|
||||
self.block_flow.build_display_list_block(layout_context)
|
||||
}
|
||||
}
|
||||
|
||||
impl Flow for TableCellFlow {
|
||||
|
@ -143,6 +138,10 @@ impl Flow for TableCellFlow {
|
|||
fn update_late_computed_block_position_if_necessary(&mut self, block_position: Au) {
|
||||
self.block_flow.update_late_computed_block_position_if_necessary(block_position)
|
||||
}
|
||||
|
||||
fn build_display_list(&mut self, layout_context: &LayoutContext) {
|
||||
self.block_flow.build_display_list(layout_context)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Show for TableCellFlow {
|
||||
|
|
|
@ -85,6 +85,9 @@ impl Flow for TableColGroupFlow {
|
|||
fn update_late_computed_inline_position_if_necessary(&mut self, _: Au) {}
|
||||
|
||||
fn update_late_computed_block_position_if_necessary(&mut self, _: Au) {}
|
||||
|
||||
// Table columns are invisible.
|
||||
fn build_display_list(&mut self, _: &LayoutContext) {}
|
||||
}
|
||||
|
||||
impl fmt::Show for TableColGroupFlow {
|
||||
|
|
|
@ -126,11 +126,6 @@ impl TableRowFlow {
|
|||
child_node.position.size.block = block_size;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_display_list_table_row(&mut self, layout_context: &LayoutContext) {
|
||||
debug!("build_display_list_table_row: same process as block flow");
|
||||
self.block_flow.build_display_list_block(layout_context)
|
||||
}
|
||||
}
|
||||
|
||||
impl Flow for TableRowFlow {
|
||||
|
@ -246,6 +241,10 @@ impl Flow for TableRowFlow {
|
|||
fn update_late_computed_block_position_if_necessary(&mut self, block_position: Au) {
|
||||
self.block_flow.update_late_computed_block_position_if_necessary(block_position)
|
||||
}
|
||||
|
||||
fn build_display_list(&mut self, layout_context: &LayoutContext) {
|
||||
self.block_flow.build_display_list(layout_context)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Show for TableRowFlow {
|
||||
|
|
|
@ -86,11 +86,6 @@ impl TableRowGroupFlow {
|
|||
self.block_flow.fragment.border_box = position;
|
||||
self.block_flow.base.position.size.block = block_size;
|
||||
}
|
||||
|
||||
pub fn build_display_list_table_rowgroup(&mut self, layout_context: &LayoutContext) {
|
||||
debug!("build_display_list_table_rowgroup: same process as block flow");
|
||||
self.block_flow.build_display_list_block(layout_context)
|
||||
}
|
||||
}
|
||||
|
||||
impl Flow for TableRowGroupFlow {
|
||||
|
@ -205,6 +200,11 @@ impl Flow for TableRowGroupFlow {
|
|||
fn update_late_computed_block_position_if_necessary(&mut self, block_position: Au) {
|
||||
self.block_flow.update_late_computed_block_position_if_necessary(block_position)
|
||||
}
|
||||
|
||||
fn build_display_list(&mut self, layout_context: &LayoutContext) {
|
||||
debug!("build_display_list_table_rowgroup: same process as block flow");
|
||||
self.block_flow.build_display_list(layout_context)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Show for TableRowGroupFlow {
|
||||
|
|
|
@ -109,11 +109,6 @@ impl TableWrapperFlow {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn build_display_list_table_wrapper(&mut self, layout_context: &LayoutContext) {
|
||||
debug!("build_display_list_table_wrapper: same process as block flow");
|
||||
self.block_flow.build_display_list_block(layout_context);
|
||||
}
|
||||
|
||||
/// Calculates table column sizes for automatic layout per INTRINSIC § 4.3.
|
||||
fn calculate_table_column_sizes_for_automatic_layout(&mut self) {
|
||||
// Find the padding and border of our first child, which is the table itself.
|
||||
|
@ -338,6 +333,10 @@ impl Flow for TableWrapperFlow {
|
|||
fn update_late_computed_block_position_if_necessary(&mut self, block_position: Au) {
|
||||
self.block_flow.update_late_computed_block_position_if_necessary(block_position)
|
||||
}
|
||||
|
||||
fn build_display_list(&mut self, layout_context: &LayoutContext) {
|
||||
self.block_flow.build_display_list(layout_context)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Show for TableWrapperFlow {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue