aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/components/main/layout/block.rs292
-rw-r--r--src/components/main/layout/box_.rs11
-rw-r--r--src/components/main/layout/construct.rs11
-rw-r--r--src/components/main/layout/inline.rs2
-rw-r--r--src/test/ref/basic.list2
-rw-r--r--src/test/ref/position_abs_nested_a.html34
-rw-r--r--src/test/ref/position_abs_nested_b.html36
-rw-r--r--src/test/ref/position_abs_replaced_simple_a.html25
-rw-r--r--src/test/ref/position_abs_replaced_simple_b.html32
9 files changed, 402 insertions, 43 deletions
diff --git a/src/components/main/layout/block.rs b/src/components/main/layout/block.rs
index 0f965a80c28..73ee6cc284b 100644
--- a/src/components/main/layout/block.rs
+++ b/src/components/main/layout/block.rs
@@ -10,8 +10,8 @@
//! The term 'positioned element' refers to elements with position =
//! 'relative', 'absolute', or 'fixed'.
-use layout::box_::Box;
-use layout::construct::{FlowConstructor, OptVector};
+use layout::box_::{Box, ImageBox, ScannedTextBox};
+use layout::construct::FlowConstructor;
use layout::context::LayoutContext;
use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData};
use layout::floats::{FloatKind, Floats, PlacementInfo};
@@ -78,6 +78,94 @@ impl WidthConstraintSolution {
margin_right: margin_right,
}
}
+
+ /// Solve the horizontal constraint equation for absolute replaced elements.
+ ///
+ /// `static_x_offset`: total offset of current flow's hypothetical
+ /// position (static position) from its actual Containing Block.
+ ///
+ /// Assumption: The used value for width has already been calculated.
+ ///
+ /// CSS Section 10.3.8
+ /// Constraint equation:
+ /// left + right + width + margin-left + margin-right
+ /// = absolute containing block width - (horizontal padding and border)
+ /// [aka available_width]
+ ///
+ /// Return the solution for the equation.
+ fn solve_horiz_constraints_abs_replaced(width: Au,
+ left_margin: MaybeAuto,
+ right_margin: MaybeAuto,
+ left: MaybeAuto,
+ right: MaybeAuto,
+ available_width: Au,
+ static_x_offset: Au)
+ -> WidthConstraintSolution {
+ // TODO: Check for direction of static-position Containing Block (aka
+ // parent flow, _not_ the actual Containing Block) when right-to-left
+ // is implemented
+ // Assume direction is 'ltr' for now
+ // TODO: Handle all the cases for 'rtl' direction.
+
+ // Distance from the left edge of the Absolute Containing Block to the
+ // left margin edge of a hypothetical box that would have been the
+ // first box of the element.
+ let static_position_left = static_x_offset;
+
+ let (left, right, width, margin_left, margin_right) = match (left, right) {
+ (Auto, Auto) => {
+ let left = static_position_left;
+ let margin_l = left_margin.specified_or_zero();
+ let margin_r = right_margin.specified_or_zero();
+ let sum = left + width + margin_l + margin_r;
+ (left, available_width - sum, width, margin_l, margin_r)
+ }
+ // If only one is Auto, solve for it
+ (Auto, Specified(right)) => {
+ let margin_l = left_margin.specified_or_zero();
+ let margin_r = right_margin.specified_or_zero();
+ let sum = right + width + margin_l + margin_r;
+ (available_width - sum, right, width, margin_l, margin_r)
+ }
+ (Specified(left), Auto) => {
+ let margin_l = left_margin.specified_or_zero();
+ let margin_r = right_margin.specified_or_zero();
+ let sum = left + width + margin_l + margin_r;
+ (left, available_width - sum, width, margin_l, margin_r)
+ }
+ (Specified(left), Specified(right)) => {
+ match (left_margin, right_margin) {
+ (Auto, Auto) => {
+ let total_margin_val = (available_width - left - right - width);
+ if total_margin_val < Au(0) {
+ // margin-left becomes 0 because direction is 'ltr'.
+ (left, right, width, Au(0), total_margin_val)
+ } else {
+ // Equal margins
+ (left, right, width,
+ total_margin_val.scale_by(0.5),
+ total_margin_val.scale_by(0.5))
+ }
+ }
+ (Specified(margin_l), Auto) => {
+ let sum = left + right + width + margin_l;
+ (left, right, width, margin_l, available_width - sum)
+ }
+ (Auto, Specified(margin_r)) => {
+ let sum = left + right + width + margin_r;
+ (left, right, width, available_width - sum, margin_r)
+ }
+ (Specified(margin_l), Specified(margin_r)) => {
+ // Values are over-constrained.
+ // Ignore value for 'right' cos direction is 'ltr'.
+ let sum = left + width + margin_l + margin_r;
+ (left, available_width - sum, width, margin_l, margin_r)
+ }
+ }
+ }
+ };
+ WidthConstraintSolution::new(left, right, width, margin_left, margin_right)
+ }
}
/// The solutions for the heights-and-margins constraint equation.
@@ -110,20 +198,20 @@ impl HeightConstraintSolution {
/// [aka available_height]
///
/// Return the solution for the equation.
- fn solve_vertical_constraints_abs_position(height: MaybeAuto,
- top_margin: MaybeAuto,
- bottom_margin: MaybeAuto,
- top: MaybeAuto,
- bottom: MaybeAuto,
- content_height: Au,
- available_height: Au,
- static_y_offset: Au)
+ fn solve_vertical_constraints_abs_nonreplaced(height: MaybeAuto,
+ top_margin: MaybeAuto,
+ bottom_margin: MaybeAuto,
+ top: MaybeAuto,
+ bottom: MaybeAuto,
+ content_height: Au,
+ available_height: Au,
+ static_y_offset: Au)
-> HeightConstraintSolution {
// Distance from the top edge of the Absolute Containing Block to the
// top margin edge of a hypothetical box that would have been the
// first box of the element.
let static_position_top = static_y_offset;
-;
+
let (top, bottom, height, margin_top, margin_bottom) = match (top, bottom, height) {
(Auto, Auto, Auto) => {
let margin_top = top_margin.specified_or_zero();
@@ -209,6 +297,80 @@ impl HeightConstraintSolution {
};
HeightConstraintSolution::new(top, bottom, height, margin_top, margin_bottom)
}
+
+ /// Solve the vertical constraint equation for absolute replaced elements.
+ ///
+ /// Assumption: The used value for height has already been calculated.
+ ///
+ /// CSS Section 10.6.5
+ /// Constraint equation:
+ /// top + bottom + height + margin-top + margin-bottom
+ /// = absolute containing block height - (vertical padding and border)
+ /// [aka available_height]
+ ///
+ /// Return the solution for the equation.
+ fn solve_vertical_constraints_abs_replaced(height: Au,
+ top_margin: MaybeAuto,
+ bottom_margin: MaybeAuto,
+ top: MaybeAuto,
+ bottom: MaybeAuto,
+ _: Au,
+ available_height: Au,
+ static_y_offset: Au)
+ -> HeightConstraintSolution {
+ // Distance from the top edge of the Absolute Containing Block to the
+ // top margin edge of a hypothetical box that would have been the
+ // first box of the element.
+ let static_position_top = static_y_offset;
+
+ let (top, bottom, height, margin_top, margin_bottom) = match (top, bottom) {
+ (Auto, Auto) => {
+ let margin_top = top_margin.specified_or_zero();
+ let margin_bottom = bottom_margin.specified_or_zero();
+ let top = static_position_top;
+ let sum = top + height + margin_top + margin_bottom;
+ (top, available_height - sum, height, margin_top, margin_bottom)
+ }
+ (Specified(top), Specified(bottom)) => {
+ match (top_margin, bottom_margin) {
+ (Auto, Auto) => {
+ let total_margin_val = (available_height - top - bottom - height);
+ (top, bottom, height,
+ total_margin_val.scale_by(0.5),
+ total_margin_val.scale_by(0.5))
+ }
+ (Specified(margin_top), Auto) => {
+ let sum = top + bottom + height + margin_top;
+ (top, bottom, height, margin_top, available_height - sum)
+ }
+ (Auto, Specified(margin_bottom)) => {
+ let sum = top + bottom + height + margin_bottom;
+ (top, bottom, height, available_height - sum, margin_bottom)
+ }
+ (Specified(margin_top), Specified(margin_bottom)) => {
+ // Values are over-constrained. Ignore value for 'bottom'.
+ let sum = top + height + margin_top + margin_bottom;
+ (top, available_height - sum, height, margin_top, margin_bottom)
+ }
+ }
+ }
+
+ // If only one is Auto, solve for it
+ (Auto, Specified(bottom)) => {
+ let margin_top = top_margin.specified_or_zero();
+ let margin_bottom = bottom_margin.specified_or_zero();
+ let sum = bottom + height + margin_top + margin_bottom;
+ (available_height - sum, bottom, height, margin_top, margin_bottom)
+ }
+ (Specified(top), Auto) => {
+ let margin_top = top_margin.specified_or_zero();
+ let margin_bottom = bottom_margin.specified_or_zero();
+ let sum = top + height + margin_top + margin_bottom;
+ (top, available_height - sum, height, margin_top, margin_bottom)
+ }
+ };
+ HeightConstraintSolution::new(top, bottom, height, margin_top, margin_bottom)
+ }
}
/// The real assign-heights traversal for flows with position 'absolute'.
@@ -424,6 +586,22 @@ impl BlockFlow {
traversal.process(flow)
}
+ /// Return true if this has a replaced box.
+ ///
+ /// The only two types of replaced boxes currently are text boxes and
+ /// image boxes.
+ fn is_replaced_content(&self) -> bool {
+ match self.box_ {
+ Some(ref box_) => {
+ match box_.specific {
+ ScannedTextBox(_) | ImageBox(_) => true,
+ _ => false,
+ }
+ }
+ None => false,
+ }
+ }
+
pub fn teardown(&mut self) {
for box_ in self.box_.iter() {
box_.teardown();
@@ -521,14 +699,28 @@ impl BlockFlow {
(MaybeAuto::from_style(style.PositionOffsets.get().left, containing_block_width),
MaybeAuto::from_style(style.PositionOffsets.get().right, containing_block_width));
let available_width = containing_block_width - box_.border_and_padding_horiz();
- // TODO: Extract this later into a SolveConstraints trait or something
- let solution = self.solve_horiz_constraints_abs_position(width,
- margin_left,
- margin_right,
- left,
- right,
- available_width,
- static_x_offset);
+
+ let solution = if self.is_replaced_content() {
+ // Calculate used value of width just like we do for inline replaced elements.
+ box_.assign_replaced_width_if_necessary(containing_block_width);
+ let width = box_.border_box.get().size.width;
+ WidthConstraintSolution::solve_horiz_constraints_abs_replaced(width,
+ margin_left,
+ margin_right,
+ left,
+ right,
+ available_width,
+ static_x_offset)
+ } else {
+ // TODO: Extract this later into a SolveConstraints trait or something
+ self.solve_horiz_constraints_abs_nonreplaced(width,
+ margin_left,
+ margin_right,
+ left,
+ right,
+ available_width,
+ static_x_offset)
+ };
let mut margin = box_.margin.get();
margin.left = solution.margin_left;
@@ -614,17 +806,18 @@ impl BlockFlow {
/// [aka available_width]
///
/// Return the solution for the equation.
- fn solve_horiz_constraints_abs_position(&self,
- width: MaybeAuto,
- left_margin: MaybeAuto,
- right_margin: MaybeAuto,
- left: MaybeAuto,
- right: MaybeAuto,
- available_width: Au,
- static_x_offset: Au)
- -> WidthConstraintSolution {
- // TODO: Check for direction of parent flow (NOT Containing Block)
- // when right-to-left is implemented.
+ fn solve_horiz_constraints_abs_nonreplaced(&self,
+ width: MaybeAuto,
+ left_margin: MaybeAuto,
+ right_margin: MaybeAuto,
+ left: MaybeAuto,
+ right: MaybeAuto,
+ available_width: Au,
+ static_x_offset: Au)
+ -> WidthConstraintSolution {
+ // TODO: Check for direction of static-position Containing Block (aka
+ // parent flow, _not_ the actual Containing Block) when right-to-left
+ // is implemented
// Assume direction is 'ltr' for now
// Distance from the left edge of the Absolute Containing Block to the
@@ -1358,15 +1551,34 @@ impl BlockFlow {
MaybeAuto::from_style(style.PositionOffsets.get().bottom, containing_block_height));
let available_height = containing_block_height - box_.border_and_padding_vert();
- let solution = HeightConstraintSolution::solve_vertical_constraints_abs_position(
- height_used_val,
- margin_top,
- margin_bottom,
- top,
- bottom,
- content_height,
- available_height,
- static_y_offset);
+ let solution = if self.is_replaced_content() {
+ // Calculate used value of height just like we do for inline replaced elements.
+ // TODO: Pass in the containing block height when Box's
+ // assign-height can handle it correctly.
+ box_.assign_replaced_height_if_necessary();
+ // TODO: Right now, this content height value includes the
+ // margin because of erroneous height calculation in Box_.
+ // Check this when that has been fixed.
+ let height_used_val = box_.border_box.get().size.height;
+ HeightConstraintSolution::solve_vertical_constraints_abs_replaced(height_used_val,
+ margin_top,
+ margin_bottom,
+ top,
+ bottom,
+ content_height,
+ available_height,
+ static_y_offset)
+ } else {
+ HeightConstraintSolution::solve_vertical_constraints_abs_nonreplaced(
+ height_used_val,
+ margin_top,
+ margin_bottom,
+ top,
+ bottom,
+ content_height,
+ available_height,
+ static_y_offset)
+ };
let mut margin = box_.margin.get();
margin.top = solution.margin_top;
@@ -1626,7 +1838,7 @@ impl Flow for BlockFlow {
fn assign_height(&mut self, ctx: &mut LayoutContext) {
// Assign height for box if it is an image box.
for box_ in self.box_.iter() {
- box_.assign_height();
+ box_.assign_replaced_height_if_necessary();
}
if self.is_float() {
diff --git a/src/components/main/layout/box_.rs b/src/components/main/layout/box_.rs
index 7b8062e5dfc..f0019d12095 100644
--- a/src/components/main/layout/box_.rs
+++ b/src/components/main/layout/box_.rs
@@ -166,6 +166,10 @@ impl ImageBoxInfo {
Au::from_px(image_ref.get().get_size().unwrap_or(Size2D(0,0)).width)
}
+ // Return used value for width or height.
+ //
+ // `dom_length`: width or height as specified in the `img` tag.
+ // `style_length`: width as given in the CSS
pub fn style_length(style_length: LengthOrPercentageOrAuto,
dom_length: Option<Au>,
container_width: Au) -> MaybeAuto {
@@ -1455,6 +1459,7 @@ impl Box {
/// Assigns replaced width for this box only if it is replaced content.
///
+ /// This assigns only the width, not margin or anything else.
/// CSS 2.1 § 10.3.2.
pub fn assign_replaced_width_if_necessary(&self,container_width: Au) {
match self.specific {
@@ -1502,8 +1507,10 @@ impl Box {
}
}
- /// Assign height for image and scanned text boxes.
- pub fn assign_height(&self) {
+ /// Assign height for this box if it is replaced content.
+ ///
+ /// Ideally, this should follow CSS 2.1 § 10.6.2
+ pub fn assign_replaced_height_if_necessary(&self) {
match self.specific {
GenericBox | IframeBox(_) => {
}
diff --git a/src/components/main/layout/construct.rs b/src/components/main/layout/construct.rs
index 745ee21c173..ffccaa02f69 100644
--- a/src/components/main/layout/construct.rs
+++ b/src/components/main/layout/construct.rs
@@ -687,6 +687,13 @@ impl<'a> FlowConstructor<'a> {
}
impl<'a> PostorderNodeMutTraversal for FlowConstructor<'a> {
+ // Construct Flow based on 'display', 'position', and 'float' values.
+ //
+ // CSS 2.1 Section 9.7
+ //
+ // TODO: This should actually consult the table in that section to get the
+ // final computed value for 'display'.
+ //
// `#[inline(always)]` because this is always called from the traversal function and for some
// reason LLVM's inlining heuristics go awry here.
#[inline(always)]
@@ -718,6 +725,10 @@ impl<'a> PostorderNodeMutTraversal for FlowConstructor<'a> {
}
}
+ // Absolutely positioned elements will have computed value of
+ // `float` as 'none' and `display` as per the table.
+ // Currently, for original `display` value of 'inline', the new
+ // `display` value is 'block'.
(_, _, position::absolute) | (_, _, position::fixed) => {
node.set_flow_construction_result(self.build_flow_for_block(node))
}
diff --git a/src/components/main/layout/inline.rs b/src/components/main/layout/inline.rs
index 6abfd6639e8..8cbc5a9eef4 100644
--- a/src/components/main/layout/inline.rs
+++ b/src/components/main/layout/inline.rs
@@ -698,7 +698,7 @@ impl Flow for InlineFlow {
debug!("assign_height_inline: floats in: {:?}", self.base.floats);
// assign height for inline boxes
for box_ in self.boxes.iter() {
- box_.assign_height();
+ box_.assign_replaced_height_if_necessary();
}
let scanner_floats = self.base.floats.clone();
let mut scanner = LineboxScanner::new(scanner_floats);
diff --git a/src/test/ref/basic.list b/src/test/ref/basic.list
index f30a45531e2..206a3319a7d 100644
--- a/src/test/ref/basic.list
+++ b/src/test/ref/basic.list
@@ -38,6 +38,8 @@
== position_abs_cb_with_non_cb_kid_a.html position_abs_cb_with_non_cb_kid_b.html
== position_abs_height_width_a.html position_abs_height_width_b.html
== position_abs_left_a.html position_abs_left_b.html
+== position_abs_nested_a.html position_abs_nested_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_width_percentage_a.html position_abs_width_percentage_b.html
== position_fixed_a.html position_fixed_b.html
diff --git a/src/test/ref/position_abs_nested_a.html b/src/test/ref/position_abs_nested_a.html
new file mode 100644
index 00000000000..4a417402362
--- /dev/null
+++ b/src/test/ref/position_abs_nested_a.html
@@ -0,0 +1,34 @@
+<html>
+ <head>
+ <style>
+ #first {
+ position: relative;
+ width: 90px;
+ height: 90px;
+ border: solid 1px;
+ }
+ #abs {
+ position: absolute;
+ left: 30px;
+ top: 30px;
+ height: 30px;
+ width: 30px;
+ background: blue;
+ }
+ #abs2 {
+ position: absolute;
+ background: green;
+ height: 15px;
+ width: 15px;
+ }
+ </style>
+ </head>
+ <body>
+ <div id="first">
+ <div id="abs">
+ <div id="abs2">
+ </div>
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/src/test/ref/position_abs_nested_b.html b/src/test/ref/position_abs_nested_b.html
new file mode 100644
index 00000000000..b539f248c8a
--- /dev/null
+++ b/src/test/ref/position_abs_nested_b.html
@@ -0,0 +1,36 @@
+<html>
+ <head>
+ <style>
+ #first {
+ width: 90px;
+ height: 90px;
+ border: solid 1px;
+ }
+ .row {
+ width: 90px;
+ height: 30px;
+ }
+ .center {
+ margin-left: 30px;
+ height: 30px;
+ width: 30px;
+ background: blue;
+ }
+ .little-box {
+ height: 15px;
+ width: 15px;
+ background: green;
+ }
+ </style>
+ </head>
+ <body>
+ <div id="first">
+ <div class="row"></div>
+ <div class="center">
+ <div class="little-box">
+ </div>
+ </div>
+ <div class="row"></div>
+ </div>
+ </body>
+</html>
diff --git a/src/test/ref/position_abs_replaced_simple_a.html b/src/test/ref/position_abs_replaced_simple_a.html
new file mode 100644
index 00000000000..0bc4feb135c
--- /dev/null
+++ b/src/test/ref/position_abs_replaced_simple_a.html
@@ -0,0 +1,25 @@
+<html>
+ <head>
+ <style>
+ #first {
+ position: relative;
+ width: 90px;
+ height: 90px;
+ border: solid 1px;
+ }
+ #abs {
+ position: absolute;
+ margin: 0px;
+ top: 30px;
+ right: 30px;
+ bottom: 30px;
+ width: 30px;
+ }
+ </style>
+ </head>
+ <body>
+ <div id="first">
+ <img src="rust_logo.png" width="100" id="abs" alt="Rust Logo" />
+ </div>
+ </body>
+</html>
diff --git a/src/test/ref/position_abs_replaced_simple_b.html b/src/test/ref/position_abs_replaced_simple_b.html
new file mode 100644
index 00000000000..da942fa82b6
--- /dev/null
+++ b/src/test/ref/position_abs_replaced_simple_b.html
@@ -0,0 +1,32 @@
+<html>
+ <head>
+ <style>
+ #first {
+ width: 90px;
+ height: 90px;
+ border: solid 1px;
+ }
+ .row {
+ width: 90px;
+ height: 30px;
+ }
+ .center {
+ margin-left: 30px;
+ height: 30px;
+ width: 30px;
+ background: green;
+ }
+ </style>
+ </head>
+ <body>
+ <div id="first">
+ <div class="row">
+ </div>
+ <div class="center">
+ <img src="rust_logo.png" width="30" id="abs" alt="Rust Logo" />
+ </div>
+ <div class="row">
+ </div>
+ </div>
+ </body>
+</html>