From 3bddaee7854a597da9d43a23d0f805ec51dac0d9 Mon Sep 17 00:00:00 2001 From: Ilyong Cho Date: Thu, 22 Aug 2013 13:57:37 +0900 Subject: [PATCH 1/3] Implement collapsing margin --- src/components/main/layout/block.rs | 52 +++++++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 2 deletions(-) diff --git a/src/components/main/layout/block.rs b/src/components/main/layout/block.rs index 8486d5225be..13b2a675b2f 100644 --- a/src/components/main/layout/block.rs +++ b/src/components/main/layout/block.rs @@ -312,17 +312,64 @@ impl BlockFlowData { } } } + + let mut collapsible = Au(0); + let mut collapsing = Au(0); + let mut first_collapsing = true; + for &box in self.box.iter() { + do box.with_model |model| { + if !self.is_root && model.border.top == Au(0) && model.padding.top == Au(0) { + collapsible = model.margin.top; + } + } + } for kid in self.common.child_iter() { + match *kid { + BlockFlow(ref info) => { + for &box in info.box.iter() { + do box.with_model |model| { + collapsing = if first_collapsing && collapsible != Au(0) { + // The top margin collapses with its first in-flow block-level child's + // top margin if the parent has no top boder, no top padding. + model.margin.top + } else { + // The bottom margin of an in-flow block-level element collapses + // with the top margin of its next in-flow block-level sibling. + geometry::min(model.margin.top, collapsible) + }; + collapsible = model.margin.bottom; + } + } + first_collapsing = false; + } + // Margins between a floated box and any other box do not collapse. + _ => { + collapsing = Au(0); + } + } + do kid.with_mut_base |child_node| { + cur_y = cur_y - collapsing; child_node.position.origin.y = cur_y; cur_y = cur_y + child_node.position.size.height; }; } + // The bottom margin collapses with its first in-flow block-level child's bottom margin + // if the parent has no bottom boder, no bottom padding. + for &box in self.box.iter() { + collapsing = do box.with_model |model| { + if !self.is_root && model.border.bottom == Au(0) && model.padding.bottom == Au(0) { + collapsible + } else { + Au(0) + } + }; + } let mut height = if self.is_root { Au::max(ctx.screen_size.size.height, cur_y) } else { - cur_y - top_offset + cur_y - top_offset - collapsing }; for &box in self.box.iter() { @@ -342,7 +389,8 @@ impl BlockFlowData { base.model.border.top + base.model.border.bottom; base.position.size.height = height + noncontent_height; - noncontent_height = noncontent_height + clearance + base.model.margin.top + base.model.margin.bottom; + noncontent_height = noncontent_height + clearance + + base.model.margin.top + base.model.margin.bottom; } }); From 7a2bbf62576f30fd0eb73d7c1ff33fe4fd520e41 Mon Sep 17 00:00:00 2001 From: Ilyong Cho Date: Wed, 28 Aug 2013 15:34:14 +0900 Subject: [PATCH 2/3] Adjust top_offset when top margin of a element collapses with its first child --- src/components/main/layout/block.rs | 67 ++++++++++++++++++++--------- 1 file changed, 46 insertions(+), 21 deletions(-) diff --git a/src/components/main/layout/block.rs b/src/components/main/layout/block.rs index 13b2a675b2f..212441c8798 100644 --- a/src/components/main/layout/block.rs +++ b/src/components/main/layout/block.rs @@ -315,12 +315,22 @@ impl BlockFlowData { let mut collapsible = Au(0); let mut collapsing = Au(0); - let mut first_collapsing = true; + let mut margin_top = Au(0); + let mut margin_bottom = Au(0); + let mut top_margin_collapsible = false; + let mut bottom_margin_collapsible = false; + let mut first_inflow = true; for &box in self.box.iter() { do box.with_model |model| { if !self.is_root && model.border.top == Au(0) && model.padding.top == Au(0) { collapsible = model.margin.top; + top_margin_collapsible = true; } + if !self.is_root && model.border.bottom == Au(0) && model.padding.bottom == Au(0) { + bottom_margin_collapsible = true; + } + margin_top = model.margin.top; + margin_bottom = model.margin.bottom; } } for kid in self.common.child_iter() { @@ -328,19 +338,26 @@ impl BlockFlowData { BlockFlow(ref info) => { for &box in info.box.iter() { do box.with_model |model| { - collapsing = if first_collapsing && collapsible != Au(0) { - // The top margin collapses with its first in-flow block-level child's - // top margin if the parent has no top boder, no top padding. - model.margin.top - } else { - // The bottom margin of an in-flow block-level element collapses - // with the top margin of its next in-flow block-level sibling. - geometry::min(model.margin.top, collapsible) - }; + // The top margin collapses with its first in-flow block-level child's + // top margin if the parent has no top boder, no top padding. + if first_inflow && top_margin_collapsible { + // If top-margin of parent is less than top-margin of its first child, + // the parent box goes down until its top is aligned with the child. + if margin_top < model.margin.top { + // TODO: The position of child floats should be updated and this + // would influence clearance as well. See #725 + let extra_margin = model.margin.top - margin_top; + top_offset = top_offset + extra_margin; + margin_top = model.margin.top; + } + } + // The bottom margin of an in-flow block-level element collapses + // with the top margin of its next in-flow block-level sibling. + collapsing = geometry::min(model.margin.top, collapsible); collapsible = model.margin.bottom; } } - first_collapsing = false; + first_inflow = false; } // Margins between a floated box and any other box do not collapse. _ => { @@ -354,17 +371,22 @@ impl BlockFlowData { cur_y = cur_y + child_node.position.size.height; }; } - // The bottom margin collapses with its first in-flow block-level child's bottom margin + + // The bottom margin collapses with its last in-flow block-level child's bottom margin // if the parent has no bottom boder, no bottom padding. - for &box in self.box.iter() { - collapsing = do box.with_model |model| { - if !self.is_root && model.border.bottom == Au(0) && model.padding.bottom == Au(0) { - collapsible - } else { - Au(0) - } - }; - } + collapsing = if bottom_margin_collapsible { + if margin_bottom < collapsible { + margin_bottom = collapsible; + } + collapsible + } else { + Au(0) + }; + + // TODO: A box's own margins collapse if the 'min-height' property is zero, and it has neither + // top or bottom borders nor top or bottom padding, and it has a 'height' of either 0 or 'auto', + // and it does not contain a line box, and all of its in-flow children's margins (if any) collapse. + let mut height = if self.is_root { Au::max(ctx.screen_size.size.height, cur_y) @@ -383,6 +405,9 @@ impl BlockFlowData { self.box.map(|&box| { do box.with_mut_base |base| { //The associated box is the border box of this flow + base.model.margin.top = margin_top; + base.model.margin.bottom = margin_bottom; + base.position.origin.y = clearance + base.model.margin.top; noncontent_height = base.model.padding.top + base.model.padding.bottom + From 3fd779054244607b37679f8b210c79455708a34b Mon Sep 17 00:00:00 2001 From: Ilyong Cho Date: Wed, 28 Aug 2013 16:22:25 +0900 Subject: [PATCH 3/3] Add reftest for margin collapsing --- src/test/ref/basic.list | 1 + src/test/ref/margin_a.html | 74 ++++++++++++++++++++++++++++++++++++++ src/test/ref/margin_b.html | 74 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 149 insertions(+) create mode 100644 src/test/ref/margin_a.html create mode 100644 src/test/ref/margin_b.html diff --git a/src/test/ref/basic.list b/src/test/ref/basic.list index e9140ef13d4..3a4742786b3 100644 --- a/src/test/ref/basic.list +++ b/src/test/ref/basic.list @@ -1,2 +1,3 @@ == basic_width_px.html basic_width_em.html == hello_a.html hello_b.html +== margin_a.html margin_b.html diff --git a/src/test/ref/margin_a.html b/src/test/ref/margin_a.html new file mode 100644 index 00000000000..fb477aa30d9 --- /dev/null +++ b/src/test/ref/margin_a.html @@ -0,0 +1,74 @@ + + + margin + + + +
+

p1

+

p2

+

p3

+
+
+

p4

+

p5

+
+
+

p6

+

p7

+

p8

+
+
+

p9

+
+
+

p10

+
+
+

p11

+
+
+

p12

+
+ + diff --git a/src/test/ref/margin_b.html b/src/test/ref/margin_b.html new file mode 100644 index 00000000000..ac8f3fa9b7c --- /dev/null +++ b/src/test/ref/margin_b.html @@ -0,0 +1,74 @@ + + + margin + + + +
+

p1

+

p2

+

p3

+
+
+

p4

+

p5

+
+
+

p6

+

p7

+

p8

+
+
+

p9

+
+
+

p10

+
+
+

p11

+
+
+

p12

+
+ +