Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

layout: Implement empty-cells per CSS 2.1 § 17.6.1.1. #4400

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
19 changes: 15 additions & 4 deletions components/layout/construct.rs
Expand Up @@ -52,7 +52,7 @@ use std::collections::DList;
use std::mem;
use std::sync::atomic::Relaxed;
use style::ComputedValues;
use style::computed_values::{display, position, float, list_style_position};
use style::computed_values::{display, empty_cells, float, list_style_position, position};
use sync::Arc;
use url::Url;

Expand Down Expand Up @@ -157,8 +157,8 @@ struct InlineFragmentsAccumulator {
/// The list of fragments.
fragments: DList<Fragment>,

/// Whether we've created a range to enclose all the fragments. This will be Some() if the outer node
/// is an inline and None otherwise.
/// Whether we've created a range to enclose all the fragments. This will be Some() if the
/// outer node is an inline and None otherwise.
enclosing_style: Option<Arc<ComputedValues>>,
}

Expand Down Expand Up @@ -914,7 +914,18 @@ impl<'a> FlowConstructor<'a> {
/// possibly other `BlockFlow`s or `InlineFlow`s underneath it.
fn build_flow_for_table_cell(&mut self, node: &ThreadSafeLayoutNode) -> ConstructionResult {
let fragment = Fragment::new_from_specific_info(node, SpecificFragmentInfo::TableCell);
let flow = box TableCellFlow::from_node_and_fragment(node, fragment) as Box<Flow>;

// Determine if the table cell should be hidden. Per CSS 2.1 § 17.6.1.1, this will be true
// if the cell has any in-flow elements (even empty ones!) and has `empty-cells` set to
// `hide`.
let hide = node.style().get_inheritedtable().empty_cells == empty_cells::hide &&
node.children().all(|kid| {
let position = kid.style().get_box().position;
!kid.is_content() || position == position::absolute || position == position::fixed
});

let flow = box TableCellFlow::from_node_fragment_and_visibility_flag(node, fragment, !hide)
as Box<Flow>;
self.build_flow_for_block(FlowRef::new(flow), node)
}

Expand Down
14 changes: 10 additions & 4 deletions components/layout/flow.rs
Expand Up @@ -59,7 +59,7 @@ use std::iter::Zip;
use std::raw;
use std::sync::atomic::{AtomicUint, SeqCst};
use std::slice::MutItems;
use style::computed_values::{clear, float, position, text_align};
use style::computed_values::{clear, empty_cells, float, position, text_align};
use style::ComputedValues;
use sync::Arc;

Expand Down Expand Up @@ -1067,12 +1067,18 @@ impl<'a> ImmutableFlowUtils for &'a Flow + 'a {
fn generate_missing_child_flow(self, node: &ThreadSafeLayoutNode) -> FlowRef {
let flow = match self.class() {
FlowClass::Table | FlowClass::TableRowGroup => {
let fragment = Fragment::new_anonymous_table_fragment(node, SpecificFragmentInfo::TableRow);
let fragment =
Fragment::new_anonymous_table_fragment(node,
SpecificFragmentInfo::TableRow);
box TableRowFlow::from_node_and_fragment(node, fragment) as Box<Flow>
},
FlowClass::TableRow => {
let fragment = Fragment::new_anonymous_table_fragment(node, SpecificFragmentInfo::TableCell);
box TableCellFlow::from_node_and_fragment(node, fragment) as Box<Flow>
let fragment =
Fragment::new_anonymous_table_fragment(node,
SpecificFragmentInfo::TableCell);
let hide = node.style().get_inheritedtable().empty_cells == empty_cells::hide;
box TableCellFlow::from_node_fragment_and_visibility_flag(node, fragment, !hide) as
Box<Flow>
},
_ => {
panic!("no need to generate a missing child")
Expand Down
20 changes: 15 additions & 5 deletions components/layout/table_cell.rs
Expand Up @@ -8,7 +8,7 @@

use block::{BlockFlow, ISizeAndMarginsComputer, MarginsMayCollapseFlag};
use context::LayoutContext;
use flow::{FlowClass, Flow};
use flow::{Flow, FlowClass};
use fragment::{Fragment, FragmentBoundsIterator};
use model::{MaybeAuto};
use layout_debug;
Expand All @@ -27,15 +27,21 @@ pub struct TableCellFlow {
pub block_flow: BlockFlow,
/// The column span of this cell.
pub column_span: u32,
/// Whether this cell is visible. If false, the value of `empty-cells` means that we must not
/// display this cell.
pub visible: bool,
}

impl TableCellFlow {
pub fn from_node_and_fragment(node: &ThreadSafeLayoutNode, fragment: Fragment)
-> TableCellFlow {
pub fn from_node_fragment_and_visibility_flag(node: &ThreadSafeLayoutNode,
fragment: Fragment,
visible: bool)
-> TableCellFlow {
TableCellFlow {
block_flow: BlockFlow::from_node_and_fragment(node, fragment),
column_span: node.get_unsigned_integer_attribute(UnsignedIntegerAttribute::ColSpanUnsignedIntegerAttribute)
.unwrap_or(1),
visible: visible,
}
}

Expand All @@ -53,7 +59,9 @@ impl TableCellFlow {
/// methods.
#[inline(always)]
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, MarginsMayCollapseFlag::MarginsMayNotCollapse)
self.block_flow.assign_block_size_block_base(
layout_context,
MarginsMayCollapseFlag::MarginsMayNotCollapse)
}
}

Expand Down Expand Up @@ -145,7 +153,9 @@ impl Flow for TableCellFlow {
}

fn build_display_list(&mut self, layout_context: &LayoutContext) {
self.block_flow.build_display_list(layout_context)
if self.visible {
self.block_flow.build_display_list(layout_context)
}
}

fn repair_style(&mut self, new_style: &Arc<ComputedValues>) {
Expand Down
9 changes: 9 additions & 0 deletions components/layout/wrapper.rs
Expand Up @@ -1000,6 +1000,15 @@ impl<'ln> ThreadSafeLayoutNode<'ln> {
_ => panic!("no layout data for this node"),
}
}

/// Returns true if this node contributes content. This is used in the implementation of
/// `empty_cells` per CSS 2.1 § 17.6.1.1.
pub fn is_content(&self) -> bool {
match self.type_id() {
Some(NodeTypeId::Element(..)) | Some(NodeTypeId::Text(..)) => true,
_ => false
}
}
}

pub struct ThreadSafeLayoutNodeChildrenIterator<'a> {
Expand Down
4 changes: 4 additions & 0 deletions components/style/properties/mod.rs.mako
Expand Up @@ -1326,6 +1326,10 @@ pub mod longhands {

${single_keyword("table-layout", "auto fixed")}

${new_style_struct("InheritedTable", is_inherited=True)}

${single_keyword("empty-cells", "show hide")}

// CSS 2.1, Section 18 - User interface


Expand Down
1 change: 1 addition & 0 deletions tests/ref/basic.list
Expand Up @@ -215,3 +215,4 @@ fragment=top != ../html/acid2.html acid2_ref.html
== legacy_table_border_attribute_a.html legacy_table_border_attribute_ref.html
== inset.html inset_ref.html
== outset.html outset_ref.html
== empty_cells_a.html empty_cells_ref.html
25 changes: 25 additions & 0 deletions tests/ref/empty_cells_a.html
@@ -0,0 +1,25 @@
<!DOCTYPE html>
<html>
<head>
<!-- Tests that `empty-cells` works—in particular, that the definition of emptiness is correct. -->
<style>
table {
empty-cells: hide;
}
section {
float: right;
}
nav {
position: absolute;
}
</style>
</head>
<body>
<table border=3>
<tr><td>Yo</td><td>Howdy</td><td><section>&nbsp;</section></td><td></td></tr>
<tr><td>Later</td><td><nav></nav></td><td>See ya</td><td>Aloha</td></tr>
<tr><td></td><td>Sayonara</td><td></td><td><span></span></td></tr>
</table>
</body>
</html>

19 changes: 19 additions & 0 deletions tests/ref/empty_cells_ref.html
@@ -0,0 +1,19 @@
<!DOCTYPE html>
<html>
<head>
<!-- Tests that `empty-cells` works—in particular, that the definition of emptiness is correct. -->
<style>
.empty {
border: none;
}
</style>
</head>
<body>
<table border=3>
<tr><td>Yo</td><td>Howdy</td><td></td><td class=empty></td></tr>
<tr><td>Later</td><td class=empty></td><td>See ya</td><td>Aloha</td></tr>
<tr><td class=empty></td><td>Sayonara</td><td class=empty></td><td></td></tr>
</table>
</body>
</html>