mirror of
https://github.com/servo/servo.git
synced 2025-08-03 20:50:07 +01:00
layout: Improve layout of table captions (#32695)
- Instead of treating captions as a `BlockFormattingContext`, treat it as a `NonReplacedFormattingContext`, which allows reusing flow layout for captions -- fixing some issues with sizing. - Pass in the proper size of the containing block when laying out, fixing margin calculation. - Follow the unspecified rules about how various size properties on captions affect their size. - Improve linebreaking around atomics, which is tested by caption-related tests. This fixes intrinsic size calculation regarding soft wrap opportunities around atomic and also makes the code making these actual soft wrap opportunities a bit better. Signed-off-by: Martin Robinson <mrobinson@igalia.com> Co-authored-by: Mukilan Thiyagarajan <mukilan@igalia.com>
This commit is contained in:
parent
2888193cfe
commit
89944bd330
52 changed files with 225 additions and 454 deletions
|
@ -103,7 +103,7 @@ impl InlineFormattingContextBuilder {
|
||||||
InlineItem::TextRun(_) => true,
|
InlineItem::TextRun(_) => true,
|
||||||
InlineItem::OutOfFlowAbsolutelyPositionedBox(_) => false,
|
InlineItem::OutOfFlowAbsolutelyPositionedBox(_) => false,
|
||||||
InlineItem::OutOfFlowFloatBox(_) => false,
|
InlineItem::OutOfFlowFloatBox(_) => false,
|
||||||
InlineItem::Atomic(_) => false,
|
InlineItem::Atomic(..) => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,7 +116,10 @@ impl InlineFormattingContextBuilder {
|
||||||
&mut self,
|
&mut self,
|
||||||
independent_formatting_context: IndependentFormattingContext,
|
independent_formatting_context: IndependentFormattingContext,
|
||||||
) -> ArcRefCell<InlineItem> {
|
) -> ArcRefCell<InlineItem> {
|
||||||
let inline_level_box = ArcRefCell::new(InlineItem::Atomic(independent_formatting_context));
|
let inline_level_box = ArcRefCell::new(InlineItem::Atomic(
|
||||||
|
independent_formatting_context,
|
||||||
|
self.current_text_offset,
|
||||||
|
));
|
||||||
self.inline_items.push(inline_level_box.clone());
|
self.inline_items.push(inline_level_box.clone());
|
||||||
|
|
||||||
// Push an object replacement character for this atomic, which will ensure that the line breaker
|
// Push an object replacement character for this atomic, which will ensure that the line breaker
|
||||||
|
|
|
@ -104,8 +104,12 @@ use style::values::specified::box_::BaselineSource;
|
||||||
use style::values::specified::text::{TextAlignKeyword, TextDecorationLine};
|
use style::values::specified::text::{TextAlignKeyword, TextDecorationLine};
|
||||||
use style::values::specified::{TextAlignLast, TextJustify};
|
use style::values::specified::{TextAlignLast, TextJustify};
|
||||||
use style::Zero;
|
use style::Zero;
|
||||||
use text_run::{add_or_get_font, get_font_for_first_font_for_style, TextRun};
|
use text_run::{
|
||||||
|
add_or_get_font, get_font_for_first_font_for_style, TextRun, XI_LINE_BREAKING_CLASS_GL,
|
||||||
|
XI_LINE_BREAKING_CLASS_WJ, XI_LINE_BREAKING_CLASS_ZWJ,
|
||||||
|
};
|
||||||
use webrender_api::FontInstanceKey;
|
use webrender_api::FontInstanceKey;
|
||||||
|
use xi_unicode::linebreak_property;
|
||||||
|
|
||||||
use super::float::PlacementAmongFloats;
|
use super::float::PlacementAmongFloats;
|
||||||
use crate::cell::ArcRefCell;
|
use crate::cell::ArcRefCell;
|
||||||
|
@ -176,7 +180,10 @@ pub(crate) enum InlineItem {
|
||||||
TextRun(TextRun),
|
TextRun(TextRun),
|
||||||
OutOfFlowAbsolutelyPositionedBox(ArcRefCell<AbsolutelyPositionedBox>),
|
OutOfFlowAbsolutelyPositionedBox(ArcRefCell<AbsolutelyPositionedBox>),
|
||||||
OutOfFlowFloatBox(FloatBox),
|
OutOfFlowFloatBox(FloatBox),
|
||||||
Atomic(IndependentFormattingContext),
|
Atomic(
|
||||||
|
IndependentFormattingContext,
|
||||||
|
usize, /* offset_in_text */
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Information about the current line under construction for a particular
|
/// Information about the current line under construction for a particular
|
||||||
|
@ -613,12 +620,6 @@ pub(super) struct InlineFormattingContextState<'a, 'b> {
|
||||||
/// is encountered.
|
/// is encountered.
|
||||||
pub have_deferred_soft_wrap_opportunity: bool,
|
pub have_deferred_soft_wrap_opportunity: bool,
|
||||||
|
|
||||||
/// Whether or not a soft wrap opportunity should be prevented before the next atomic
|
|
||||||
/// element encountered in the inline formatting context. See
|
|
||||||
/// `char_prevents_soft_wrap_opportunity_when_before_or_after_atomic` for more
|
|
||||||
/// details.
|
|
||||||
pub prevent_soft_wrap_opportunity_before_next_atomic: bool,
|
|
||||||
|
|
||||||
/// Whether or not this InlineFormattingContext has processed any in flow content at all.
|
/// Whether or not this InlineFormattingContext has processed any in flow content at all.
|
||||||
had_inflow_content: bool,
|
had_inflow_content: bool,
|
||||||
|
|
||||||
|
@ -1202,15 +1203,11 @@ impl<'a, 'b> InlineFormattingContextState<'a, 'b> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn defer_forced_line_break(&mut self) {
|
pub(super) fn defer_forced_line_break(&mut self) {
|
||||||
// If this hard line break happens in the middle of an unbreakable segment, there are two
|
// If the current portion of the unbreakable segment does not fit on the current line
|
||||||
// scenarios:
|
// we need to put it on a new line *before* actually triggering the hard line break.
|
||||||
// 1. The current portion of the unbreakable segment fits on the current line in which
|
if !self.unbreakable_segment_fits_on_line() {
|
||||||
// case we commit it.
|
self.process_line_break(false /* forced_line_break */);
|
||||||
// 2. The current portion of the unbreakable segment does not fit in which case we
|
}
|
||||||
// need to put it on a new line *before* actually triggering the hard line break.
|
|
||||||
//
|
|
||||||
// `process_soft_wrap_opportunity` handles both of these cases.
|
|
||||||
self.process_soft_wrap_opportunity();
|
|
||||||
|
|
||||||
// Defer the actual line break until we've cleared all ending inline boxes.
|
// Defer the actual line break until we've cleared all ending inline boxes.
|
||||||
self.linebreak_before_new_content = true;
|
self.linebreak_before_new_content = true;
|
||||||
|
@ -1371,6 +1368,20 @@ impl<'a, 'b> InlineFormattingContextState<'a, 'b> {
|
||||||
self.finish_current_line_and_reset(forced_line_break);
|
self.finish_current_line_and_reset(forced_line_break);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn unbreakable_segment_fits_on_line(&mut self) -> bool {
|
||||||
|
let potential_line_size = LogicalVec2 {
|
||||||
|
inline: self.current_line.inline_position + self.current_line_segment.inline_size -
|
||||||
|
self.current_line_segment.trailing_whitespace_size,
|
||||||
|
block: self
|
||||||
|
.current_line_max_block_size_including_nested_containers()
|
||||||
|
.max(&self.current_line_segment.max_block_size)
|
||||||
|
.resolve()
|
||||||
|
.into(),
|
||||||
|
};
|
||||||
|
|
||||||
|
!self.new_potential_line_size_causes_line_break(&potential_line_size)
|
||||||
|
}
|
||||||
|
|
||||||
/// Process a soft wrap opportunity. This will either commit the current unbreakble
|
/// Process a soft wrap opportunity. This will either commit the current unbreakble
|
||||||
/// segment to the current line, if it fits within the containing block and float
|
/// segment to the current line, if it fits within the containing block and float
|
||||||
/// placement boundaries, or do a line break and then commit the segment.
|
/// placement boundaries, or do a line break and then commit the segment.
|
||||||
|
@ -1633,7 +1644,6 @@ impl InlineFormattingContext {
|
||||||
linebreak_before_new_content: false,
|
linebreak_before_new_content: false,
|
||||||
deferred_br_clear: Clear::None,
|
deferred_br_clear: Clear::None,
|
||||||
have_deferred_soft_wrap_opportunity: false,
|
have_deferred_soft_wrap_opportunity: false,
|
||||||
prevent_soft_wrap_opportunity_before_next_atomic: false,
|
|
||||||
had_inflow_content: false,
|
had_inflow_content: false,
|
||||||
white_space_collapse: style_text.white_space_collapse,
|
white_space_collapse: style_text.white_space_collapse,
|
||||||
text_wrap_mode: style_text.text_wrap_mode,
|
text_wrap_mode: style_text.text_wrap_mode,
|
||||||
|
@ -1662,8 +1672,13 @@ impl InlineFormattingContext {
|
||||||
},
|
},
|
||||||
InlineItem::EndInlineBox => ifc.finish_inline_box(),
|
InlineItem::EndInlineBox => ifc.finish_inline_box(),
|
||||||
InlineItem::TextRun(run) => run.layout_into_line_items(&mut ifc),
|
InlineItem::TextRun(run) => run.layout_into_line_items(&mut ifc),
|
||||||
InlineItem::Atomic(atomic_formatting_context) => {
|
InlineItem::Atomic(atomic_formatting_context, offset_in_text) => {
|
||||||
atomic_formatting_context.layout_into_line_items(layout_context, &mut ifc);
|
atomic_formatting_context.layout_into_line_items(
|
||||||
|
layout_context,
|
||||||
|
self,
|
||||||
|
&mut ifc,
|
||||||
|
*offset_in_text,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box) => {
|
InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box) => {
|
||||||
ifc.push_line_item_to_unbreakable_segment(LineItem::AbsolutelyPositioned(
|
ifc.push_line_item_to_unbreakable_segment(LineItem::AbsolutelyPositioned(
|
||||||
|
@ -1694,6 +1709,20 @@ impl InlineFormattingContext {
|
||||||
baselines: ifc.baselines,
|
baselines: ifc.baselines,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn next_character_prevents_soft_wrap_opportunity(&self, index: usize) -> bool {
|
||||||
|
let Some(character) = self.text_content[index..].chars().skip(1).next() else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
char_prevents_soft_wrap_opportunity_when_before_or_after_atomic(character)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn previous_character_prevents_soft_wrap_opportunity(&self, index: usize) -> bool {
|
||||||
|
let Some(character) = self.text_content[0..index].chars().rev().next() else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
char_prevents_soft_wrap_opportunity_when_before_or_after_atomic(character)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InlineContainerState {
|
impl InlineContainerState {
|
||||||
|
@ -1894,10 +1923,12 @@ impl IndependentFormattingContext {
|
||||||
fn layout_into_line_items(
|
fn layout_into_line_items(
|
||||||
&mut self,
|
&mut self,
|
||||||
layout_context: &LayoutContext,
|
layout_context: &LayoutContext,
|
||||||
ifc: &mut InlineFormattingContextState,
|
inline_formatting_context: &InlineFormattingContext,
|
||||||
|
inline_formatting_context_state: &mut InlineFormattingContextState,
|
||||||
|
offset_in_text: usize,
|
||||||
) {
|
) {
|
||||||
let style = self.style();
|
let style = self.style();
|
||||||
let pbm = style.padding_border_margin(ifc.containing_block);
|
let pbm = style.padding_border_margin(inline_formatting_context_state.containing_block);
|
||||||
let margin = pbm.margin.auto_is(Au::zero);
|
let margin = pbm.margin.auto_is(Au::zero);
|
||||||
let pbm_sums = pbm.padding + pbm.border + margin;
|
let pbm_sums = pbm.padding + pbm.border + margin;
|
||||||
let mut child_positioning_context = None;
|
let mut child_positioning_context = None;
|
||||||
|
@ -1906,7 +1937,7 @@ impl IndependentFormattingContext {
|
||||||
let fragment = match self {
|
let fragment = match self {
|
||||||
IndependentFormattingContext::Replaced(replaced) => {
|
IndependentFormattingContext::Replaced(replaced) => {
|
||||||
let size = replaced.contents.used_size_as_if_inline_element(
|
let size = replaced.contents.used_size_as_if_inline_element(
|
||||||
ifc.containing_block,
|
inline_formatting_context_state.containing_block,
|
||||||
&replaced.style,
|
&replaced.style,
|
||||||
None,
|
None,
|
||||||
&pbm,
|
&pbm,
|
||||||
|
@ -1932,18 +1963,20 @@ impl IndependentFormattingContext {
|
||||||
IndependentFormattingContext::NonReplaced(non_replaced) => {
|
IndependentFormattingContext::NonReplaced(non_replaced) => {
|
||||||
let box_size = non_replaced
|
let box_size = non_replaced
|
||||||
.style
|
.style
|
||||||
.content_box_size(ifc.containing_block, &pbm);
|
.content_box_size(inline_formatting_context_state.containing_block, &pbm);
|
||||||
let max_box_size = non_replaced
|
let max_box_size = non_replaced
|
||||||
.style
|
.style
|
||||||
.content_max_box_size(ifc.containing_block, &pbm);
|
.content_max_box_size(inline_formatting_context_state.containing_block, &pbm);
|
||||||
let min_box_size = non_replaced
|
let min_box_size = non_replaced
|
||||||
.style
|
.style
|
||||||
.content_min_box_size(ifc.containing_block, &pbm)
|
.content_min_box_size(inline_formatting_context_state.containing_block, &pbm)
|
||||||
.auto_is(Length::zero);
|
.auto_is(Length::zero);
|
||||||
|
|
||||||
// https://drafts.csswg.org/css2/visudet.html#inlineblock-width
|
// https://drafts.csswg.org/css2/visudet.html#inlineblock-width
|
||||||
let tentative_inline_size = box_size.inline.auto_is(|| {
|
let tentative_inline_size = box_size.inline.auto_is(|| {
|
||||||
let available_size = ifc.containing_block.inline_size - pbm_sums.inline_sum();
|
let available_size =
|
||||||
|
inline_formatting_context_state.containing_block.inline_size -
|
||||||
|
pbm_sums.inline_sum();
|
||||||
non_replaced
|
non_replaced
|
||||||
.inline_content_sizes(layout_context)
|
.inline_content_sizes(layout_context)
|
||||||
.shrink_to_fit(available_size)
|
.shrink_to_fit(available_size)
|
||||||
|
@ -1962,7 +1995,10 @@ impl IndependentFormattingContext {
|
||||||
style: &non_replaced.style,
|
style: &non_replaced.style,
|
||||||
};
|
};
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
ifc.containing_block.style.writing_mode,
|
inline_formatting_context_state
|
||||||
|
.containing_block
|
||||||
|
.style
|
||||||
|
.writing_mode,
|
||||||
containing_block_for_children.style.writing_mode,
|
containing_block_for_children.style.writing_mode,
|
||||||
"Mixed writing modes are not supported yet"
|
"Mixed writing modes are not supported yet"
|
||||||
);
|
);
|
||||||
|
@ -1977,7 +2013,7 @@ impl IndependentFormattingContext {
|
||||||
layout_context,
|
layout_context,
|
||||||
child_positioning_context.as_mut().unwrap(),
|
child_positioning_context.as_mut().unwrap(),
|
||||||
&containing_block_for_children,
|
&containing_block_for_children,
|
||||||
ifc.containing_block,
|
inline_formatting_context_state.containing_block,
|
||||||
);
|
);
|
||||||
let (inline_size, block_size) =
|
let (inline_size, block_size) =
|
||||||
match independent_layout.content_inline_size_for_table {
|
match independent_layout.content_inline_size_for_table {
|
||||||
|
@ -2021,12 +2057,11 @@ impl IndependentFormattingContext {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let soft_wrap_opportunity_prevented = mem::replace(
|
if inline_formatting_context_state.text_wrap_mode == TextWrapMode::Wrap &&
|
||||||
&mut ifc.prevent_soft_wrap_opportunity_before_next_atomic,
|
!inline_formatting_context
|
||||||
false,
|
.previous_character_prevents_soft_wrap_opportunity(offset_in_text)
|
||||||
);
|
{
|
||||||
if ifc.text_wrap_mode == TextWrapMode::Wrap && !soft_wrap_opportunity_prevented {
|
inline_formatting_context_state.process_soft_wrap_opportunity();
|
||||||
ifc.process_soft_wrap_opportunity();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let size = pbm_sums.sum() + fragment.content_rect.size;
|
let size = pbm_sums.sum() + fragment.content_rect.size;
|
||||||
|
@ -2035,15 +2070,18 @@ impl IndependentFormattingContext {
|
||||||
.map(|baseline| pbm_sums.block_start + baseline)
|
.map(|baseline| pbm_sums.block_start + baseline)
|
||||||
.unwrap_or(size.block);
|
.unwrap_or(size.block);
|
||||||
|
|
||||||
let (block_sizes, baseline_offset_in_parent) =
|
let (block_sizes, baseline_offset_in_parent) = self.get_block_sizes_and_baseline_offset(
|
||||||
self.get_block_sizes_and_baseline_offset(ifc, size.block.into(), baseline_offset);
|
inline_formatting_context_state,
|
||||||
ifc.update_unbreakable_segment_for_new_content(
|
size.block.into(),
|
||||||
|
baseline_offset,
|
||||||
|
);
|
||||||
|
inline_formatting_context_state.update_unbreakable_segment_for_new_content(
|
||||||
&block_sizes,
|
&block_sizes,
|
||||||
size.inline.into(),
|
size.inline.into(),
|
||||||
SegmentContentFlags::empty(),
|
SegmentContentFlags::empty(),
|
||||||
);
|
);
|
||||||
ifc.push_line_item_to_unbreakable_segment(LineItem::Atomic(
|
inline_formatting_context_state.push_line_item_to_unbreakable_segment(LineItem::Atomic(
|
||||||
ifc.current_inline_box_identifier(),
|
inline_formatting_context_state.current_inline_box_identifier(),
|
||||||
AtomicLineItem {
|
AtomicLineItem {
|
||||||
fragment,
|
fragment,
|
||||||
size,
|
size,
|
||||||
|
@ -2053,8 +2091,12 @@ impl IndependentFormattingContext {
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
|
|
||||||
// Defer a soft wrap opportunity for when we next process text content.
|
// If there's a soft wrap opportunity following this atomic, defer a soft wrap opportunity
|
||||||
ifc.have_deferred_soft_wrap_opportunity = true;
|
// for when we next process text content.
|
||||||
|
if !inline_formatting_context.next_character_prevents_soft_wrap_opportunity(offset_in_text)
|
||||||
|
{
|
||||||
|
inline_formatting_context_state.have_deferred_soft_wrap_opportunity = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Picks either the first or the last baseline, depending on `baseline-source`.
|
/// Picks either the first or the last baseline, depending on `baseline-source`.
|
||||||
|
@ -2345,12 +2387,25 @@ impl<'a> ContentSizesComputation<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
InlineItem::Atomic(atomic) => {
|
InlineItem::Atomic(atomic, offset_in_text) => {
|
||||||
|
// TODO: need to handle TextWrapMode::Nowrap.
|
||||||
|
if !inline_formatting_context
|
||||||
|
.previous_character_prevents_soft_wrap_opportunity(*offset_in_text)
|
||||||
|
{
|
||||||
|
self.line_break_opportunity();
|
||||||
|
}
|
||||||
|
|
||||||
let outer = atomic.outer_inline_content_sizes(
|
let outer = atomic.outer_inline_content_sizes(
|
||||||
self.layout_context,
|
self.layout_context,
|
||||||
self.containing_block_writing_mode,
|
self.containing_block_writing_mode,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if !inline_formatting_context
|
||||||
|
.next_character_prevents_soft_wrap_opportunity(*offset_in_text)
|
||||||
|
{
|
||||||
|
self.line_break_opportunity();
|
||||||
|
}
|
||||||
|
|
||||||
self.commit_pending_whitespace();
|
self.commit_pending_whitespace();
|
||||||
self.current_line += outer;
|
self.current_line += outer;
|
||||||
self.had_content_yet = true;
|
self.had_content_yet = true;
|
||||||
|
@ -2404,3 +2459,24 @@ impl<'a> ContentSizesComputation<'a> {
|
||||||
.traverse(inline_formatting_context)
|
.traverse(inline_formatting_context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether or not this character will rpevent a soft wrap opportunity when it
|
||||||
|
/// comes before or after an atomic inline element.
|
||||||
|
///
|
||||||
|
/// From <https://www.w3.org/TR/css-text-3/#line-break-details>:
|
||||||
|
///
|
||||||
|
/// > For Web-compatibility there is a soft wrap opportunity before and after each
|
||||||
|
/// > replaced element or other atomic inline, even when adjacent to a character that
|
||||||
|
/// > would normally suppress them, including U+00A0 NO-BREAK SPACE. However, with
|
||||||
|
/// > the exception of U+00A0 NO-BREAK SPACE, there must be no soft wrap opportunity
|
||||||
|
/// > between atomic inlines and adjacent characters belonging to the Unicode GL, WJ,
|
||||||
|
/// > or ZWJ line breaking classes.
|
||||||
|
fn char_prevents_soft_wrap_opportunity_when_before_or_after_atomic(character: char) -> bool {
|
||||||
|
if character == '\u{00A0}' {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let class = linebreak_property(character);
|
||||||
|
class == XI_LINE_BREAKING_CLASS_GL ||
|
||||||
|
class == XI_LINE_BREAKING_CLASS_WJ ||
|
||||||
|
class == XI_LINE_BREAKING_CLASS_ZWJ
|
||||||
|
}
|
||||||
|
|
|
@ -30,11 +30,11 @@ use crate::fragment_tree::BaseFragmentInfo;
|
||||||
|
|
||||||
// These constants are the xi-unicode line breaking classes that are defined in
|
// These constants are the xi-unicode line breaking classes that are defined in
|
||||||
// `table.rs`. Unfortunately, they are only identified by number.
|
// `table.rs`. Unfortunately, they are only identified by number.
|
||||||
const XI_LINE_BREAKING_CLASS_CM: u8 = 9;
|
pub(crate) const XI_LINE_BREAKING_CLASS_CM: u8 = 9;
|
||||||
const XI_LINE_BREAKING_CLASS_GL: u8 = 12;
|
pub(crate) const XI_LINE_BREAKING_CLASS_GL: u8 = 12;
|
||||||
const XI_LINE_BREAKING_CLASS_ZW: u8 = 28;
|
pub(crate) const XI_LINE_BREAKING_CLASS_ZW: u8 = 28;
|
||||||
const XI_LINE_BREAKING_CLASS_WJ: u8 = 30;
|
pub(crate) const XI_LINE_BREAKING_CLASS_WJ: u8 = 30;
|
||||||
const XI_LINE_BREAKING_CLASS_ZWJ: u8 = 40;
|
pub(crate) const XI_LINE_BREAKING_CLASS_ZWJ: u8 = 40;
|
||||||
|
|
||||||
/// <https://www.w3.org/TR/css-display-3/#css-text-run>
|
/// <https://www.w3.org/TR/css-display-3/#css-text-run>
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
|
@ -47,16 +47,6 @@ pub(crate) struct TextRun {
|
||||||
/// The text of this [`TextRun`] with a font selected, broken into unbreakable
|
/// The text of this [`TextRun`] with a font selected, broken into unbreakable
|
||||||
/// segments, and shaped.
|
/// segments, and shaped.
|
||||||
pub shaped_text: Vec<TextRunSegment>,
|
pub shaped_text: Vec<TextRunSegment>,
|
||||||
|
|
||||||
/// Whether or not to prevent a soft wrap opportunity at the start of this [`TextRun`].
|
|
||||||
/// This depends on the whether the first character in the run prevents a soft wrap
|
|
||||||
/// opportunity.
|
|
||||||
prevent_soft_wrap_opportunity_at_start: bool,
|
|
||||||
|
|
||||||
/// Whether or not to prevent a soft wrap opportunity at the end of this [`TextRun`].
|
|
||||||
/// This depends on the whether the last character in the run prevents a soft wrap
|
|
||||||
/// opportunity.
|
|
||||||
prevent_soft_wrap_opportunity_at_end: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// There are two reasons why we might want to break at the start:
|
// There are two reasons why we might want to break at the start:
|
||||||
|
@ -70,7 +60,6 @@ pub(crate) struct TextRun {
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
enum SegmentStartSoftWrapPolicy {
|
enum SegmentStartSoftWrapPolicy {
|
||||||
Force,
|
Force,
|
||||||
Prevent,
|
|
||||||
FollowLinebreaker,
|
FollowLinebreaker,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -153,13 +142,11 @@ impl TextRunSegment {
|
||||||
ifc.defer_forced_line_break();
|
ifc.defer_forced_line_break();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Break before each unbreakable run in this TextRun, except the first unless the
|
// Break before each unbreakable run in this TextRun, except the first unless the
|
||||||
// linebreaker was set to break before the first run.
|
// linebreaker was set to break before the first run.
|
||||||
if run_index != 0 || soft_wrap_policy == SegmentStartSoftWrapPolicy::Force {
|
if run_index != 0 || soft_wrap_policy == SegmentStartSoftWrapPolicy::Force {
|
||||||
ifc.process_soft_wrap_opportunity();
|
ifc.process_soft_wrap_opportunity();
|
||||||
}
|
}
|
||||||
|
|
||||||
ifc.push_glyph_store_to_unbreakable_segment(
|
ifc.push_glyph_store_to_unbreakable_segment(
|
||||||
run.glyph_store.clone(),
|
run.glyph_store.clone(),
|
||||||
text_run,
|
text_run,
|
||||||
|
@ -332,8 +319,6 @@ impl TextRun {
|
||||||
parent_style,
|
parent_style,
|
||||||
text_range,
|
text_range,
|
||||||
shaped_text: Vec::new(),
|
shaped_text: Vec::new(),
|
||||||
prevent_soft_wrap_opportunity_at_start: false,
|
|
||||||
prevent_soft_wrap_opportunity_at_end: false,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -417,13 +402,6 @@ impl TextRun {
|
||||||
let current_byte_index = next_byte_index;
|
let current_byte_index = next_byte_index;
|
||||||
next_byte_index += character.len_utf8();
|
next_byte_index += character.len_utf8();
|
||||||
|
|
||||||
let prevents_soft_wrap_opportunity =
|
|
||||||
char_prevents_soft_wrap_opportunity_when_before_or_after_atomic(character);
|
|
||||||
if current_byte_index == self.text_range.start && prevents_soft_wrap_opportunity {
|
|
||||||
self.prevent_soft_wrap_opportunity_at_start = true;
|
|
||||||
}
|
|
||||||
self.prevent_soft_wrap_opportunity_at_end = prevents_soft_wrap_opportunity;
|
|
||||||
|
|
||||||
if char_does_not_change_font(character) {
|
if char_does_not_change_font(character) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -496,9 +474,8 @@ impl TextRun {
|
||||||
// character it should also override the LineBreaker's indication to break at the start.
|
// character it should also override the LineBreaker's indication to break at the start.
|
||||||
let have_deferred_soft_wrap_opportunity =
|
let have_deferred_soft_wrap_opportunity =
|
||||||
mem::replace(&mut ifc.have_deferred_soft_wrap_opportunity, false);
|
mem::replace(&mut ifc.have_deferred_soft_wrap_opportunity, false);
|
||||||
let mut soft_wrap_policy = match self.prevent_soft_wrap_opportunity_at_start {
|
let mut soft_wrap_policy = match have_deferred_soft_wrap_opportunity {
|
||||||
true => SegmentStartSoftWrapPolicy::Prevent,
|
true => SegmentStartSoftWrapPolicy::Force,
|
||||||
false if have_deferred_soft_wrap_opportunity => SegmentStartSoftWrapPolicy::Force,
|
|
||||||
false => SegmentStartSoftWrapPolicy::FollowLinebreaker,
|
false => SegmentStartSoftWrapPolicy::FollowLinebreaker,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -506,33 +483,9 @@ impl TextRun {
|
||||||
segment.layout_into_line_items(self, soft_wrap_policy, ifc);
|
segment.layout_into_line_items(self, soft_wrap_policy, ifc);
|
||||||
soft_wrap_policy = SegmentStartSoftWrapPolicy::FollowLinebreaker;
|
soft_wrap_policy = SegmentStartSoftWrapPolicy::FollowLinebreaker;
|
||||||
}
|
}
|
||||||
|
|
||||||
ifc.prevent_soft_wrap_opportunity_before_next_atomic =
|
|
||||||
self.prevent_soft_wrap_opportunity_at_end;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether or not this character will rpevent a soft wrap opportunity when it
|
|
||||||
/// comes before or after an atomic inline element.
|
|
||||||
///
|
|
||||||
/// From <https://www.w3.org/TR/css-text-3/#line-break-details>:
|
|
||||||
///
|
|
||||||
/// > For Web-compatibility there is a soft wrap opportunity before and after each
|
|
||||||
/// > replaced element or other atomic inline, even when adjacent to a character that
|
|
||||||
/// > would normally suppress them, including U+00A0 NO-BREAK SPACE. However, with
|
|
||||||
/// > the exception of U+00A0 NO-BREAK SPACE, there must be no soft wrap opportunity
|
|
||||||
/// > between atomic inlines and adjacent characters belonging to the Unicode GL, WJ,
|
|
||||||
/// > or ZWJ line breaking classes.
|
|
||||||
fn char_prevents_soft_wrap_opportunity_when_before_or_after_atomic(character: char) -> bool {
|
|
||||||
if character == '\u{00A0}' {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
let class = linebreak_property(character);
|
|
||||||
class == XI_LINE_BREAKING_CLASS_GL ||
|
|
||||||
class == XI_LINE_BREAKING_CLASS_WJ ||
|
|
||||||
class == XI_LINE_BREAKING_CLASS_ZWJ
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Whether or not this character should be able to change the font during segmentation. Certain
|
/// Whether or not this character should be able to change the font during segmentation. Certain
|
||||||
/// character are not rendered at all, so it doesn't matter what font we use to render them. They
|
/// character are not rendered at all, so it doesn't matter what font we use to render them. They
|
||||||
/// should just be added to the current segment.
|
/// should just be added to the current segment.
|
||||||
|
|
|
@ -926,7 +926,7 @@ impl NonReplacedFormattingContext {
|
||||||
///
|
///
|
||||||
/// - <https://drafts.csswg.org/css2/visudet.html#blockwidth>
|
/// - <https://drafts.csswg.org/css2/visudet.html#blockwidth>
|
||||||
/// - <https://drafts.csswg.org/css2/visudet.html#normal-block>
|
/// - <https://drafts.csswg.org/css2/visudet.html#normal-block>
|
||||||
fn layout_in_flow_block_level(
|
pub(crate) fn layout_in_flow_block_level(
|
||||||
&self,
|
&self,
|
||||||
layout_context: &LayoutContext,
|
layout_context: &LayoutContext,
|
||||||
positioning_context: &mut PositioningContext,
|
positioning_context: &mut PositioningContext,
|
||||||
|
@ -1367,7 +1367,7 @@ struct ContainingBlockPaddingAndBorder<'a> {
|
||||||
max_box_size: LogicalVec2<Option<Length>>,
|
max_box_size: LogicalVec2<Option<Length>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct ResolvedMargins {
|
struct ResolvedMargins {
|
||||||
/// Used value for the margin properties, as exposed in getComputedStyle().
|
/// Used value for the margin properties, as exposed in getComputedStyle().
|
||||||
pub margin: LogicalSides<Au>,
|
pub margin: LogicalSides<Au>,
|
||||||
|
|
||||||
|
@ -1440,7 +1440,7 @@ fn solve_containing_block_padding_and_border_for_in_flow_box<'a>(
|
||||||
/// Note that in the presence of floats, this shouldn't be used for a block-level box
|
/// Note that in the presence of floats, this shouldn't be used for a block-level box
|
||||||
/// that establishes an independent formatting context (or is replaced), since the
|
/// that establishes an independent formatting context (or is replaced), since the
|
||||||
/// margins could then be incorrect.
|
/// margins could then be incorrect.
|
||||||
pub(crate) fn solve_margins(
|
fn solve_margins(
|
||||||
containing_block: &ContainingBlock<'_>,
|
containing_block: &ContainingBlock<'_>,
|
||||||
pbm: &PaddingBorderMargin,
|
pbm: &PaddingBorderMargin,
|
||||||
inline_size: Au,
|
inline_size: Au,
|
||||||
|
|
|
@ -18,6 +18,7 @@ use super::{
|
||||||
Table, TableCaption, TableSlot, TableSlotCell, TableSlotCoordinates, TableSlotOffset,
|
Table, TableCaption, TableSlot, TableSlotCell, TableSlotCoordinates, TableSlotOffset,
|
||||||
TableTrack, TableTrackGroup, TableTrackGroupType,
|
TableTrack, TableTrackGroup, TableTrackGroupType,
|
||||||
};
|
};
|
||||||
|
use crate::cell::ArcRefCell;
|
||||||
use crate::context::LayoutContext;
|
use crate::context::LayoutContext;
|
||||||
use crate::dom::{BoxSlot, NodeExt};
|
use crate::dom::{BoxSlot, NodeExt};
|
||||||
use crate::dom_traversal::{Contents, NodeAndStyleInfo, NonReplacedContents, TraversalHandler};
|
use crate::dom_traversal::{Contents, NodeAndStyleInfo, NonReplacedContents, TraversalHandler};
|
||||||
|
@ -842,23 +843,31 @@ where
|
||||||
DisplayLayoutInternal::TableCaption => {
|
DisplayLayoutInternal::TableCaption => {
|
||||||
let contents = match contents.try_into() {
|
let contents = match contents.try_into() {
|
||||||
Ok(non_replaced_contents) => {
|
Ok(non_replaced_contents) => {
|
||||||
|
NonReplacedFormattingContextContents::Flow(
|
||||||
BlockFormattingContext::construct(
|
BlockFormattingContext::construct(
|
||||||
self.context,
|
self.context,
|
||||||
info,
|
info,
|
||||||
non_replaced_contents,
|
non_replaced_contents,
|
||||||
self.current_text_decoration_line,
|
self.current_text_decoration_line,
|
||||||
false, /* is_list_item */
|
false, /* is_list_item */
|
||||||
|
),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
Err(_replaced) => {
|
Err(_replaced) => {
|
||||||
unreachable!("Replaced should not have a LayoutInternal display type.");
|
unreachable!("Replaced should not have a LayoutInternal display type.");
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
self.builder.table.captions.push(TableCaption {
|
|
||||||
contents,
|
let caption = TableCaption {
|
||||||
|
context: ArcRefCell::new(NonReplacedFormattingContext {
|
||||||
style: info.style.clone(),
|
style: info.style.clone(),
|
||||||
base_fragment_info: info.into(),
|
base_fragment_info: info.into(),
|
||||||
});
|
content_sizes: None,
|
||||||
|
contents,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
self.builder.table.captions.push(caption);
|
||||||
|
|
||||||
// We are doing this until we have actually set a Box for this `BoxSlot`.
|
// We are doing this until we have actually set a Box for this `BoxSlot`.
|
||||||
::std::mem::forget(box_slot)
|
::std::mem::forget(box_slot)
|
||||||
|
|
|
@ -22,7 +22,6 @@ use style::Zero;
|
||||||
|
|
||||||
use super::{Table, TableCaption, TableSlot, TableSlotCell, TableTrack, TableTrackGroup};
|
use super::{Table, TableCaption, TableSlot, TableSlotCell, TableTrack, TableTrackGroup};
|
||||||
use crate::context::LayoutContext;
|
use crate::context::LayoutContext;
|
||||||
use crate::flow::{solve_margins, ResolvedMargins};
|
|
||||||
use crate::formatting_contexts::{Baselines, IndependentLayout};
|
use crate::formatting_contexts::{Baselines, IndependentLayout};
|
||||||
use crate::fragment_tree::{
|
use crate::fragment_tree::{
|
||||||
BaseFragmentInfo, BoxFragment, CollapsedBlockMargins, ExtraBackground, Fragment, FragmentFlags,
|
BaseFragmentInfo, BoxFragment, CollapsedBlockMargins, ExtraBackground, Fragment, FragmentFlags,
|
||||||
|
@ -105,7 +104,11 @@ pub(crate) struct TableLayout<'a> {
|
||||||
rows: Vec<RowLayout>,
|
rows: Vec<RowLayout>,
|
||||||
columns: Vec<ColumnLayout>,
|
columns: Vec<ColumnLayout>,
|
||||||
cell_measures: Vec<Vec<LogicalVec2<CellOrTrackMeasure>>>,
|
cell_measures: Vec<Vec<LogicalVec2<CellOrTrackMeasure>>>,
|
||||||
|
/// The calculated width of the table, including space for the grid and also for any
|
||||||
|
/// captions.
|
||||||
table_width: Au,
|
table_width: Au,
|
||||||
|
/// The table width minus the total horizontal border spacing (if any). This is the
|
||||||
|
/// width that we will be able to allocate to the columns.
|
||||||
assignable_width: Au,
|
assignable_width: Au,
|
||||||
final_table_height: Au,
|
final_table_height: Au,
|
||||||
column_measures: Vec<CellOrTrackMeasure>,
|
column_measures: Vec<CellOrTrackMeasure>,
|
||||||
|
@ -674,36 +677,52 @@ impl<'a> TableLayout<'a> {
|
||||||
.captions
|
.captions
|
||||||
.iter()
|
.iter()
|
||||||
.map(|caption| {
|
.map(|caption| {
|
||||||
let padding = caption
|
let size;
|
||||||
|
let min_size;
|
||||||
|
let max_size;
|
||||||
|
let padding_border_sums;
|
||||||
|
let size_is_auto;
|
||||||
|
{
|
||||||
|
let context = caption.context.borrow();
|
||||||
|
let padding = context
|
||||||
.style
|
.style
|
||||||
.padding(writing_mode)
|
.padding(writing_mode)
|
||||||
.percentages_relative_to(Length::zero());
|
.percentages_relative_to(Length::zero());
|
||||||
let border = caption.style.border_width(writing_mode);
|
let border = context.style.border_width(writing_mode);
|
||||||
let margin = caption
|
let margin = context
|
||||||
.style
|
.style
|
||||||
.margin(writing_mode)
|
.margin(writing_mode)
|
||||||
.percentages_relative_to(Length::zero())
|
.percentages_relative_to(Length::zero())
|
||||||
.auto_is(Length::zero);
|
.auto_is(Length::zero);
|
||||||
|
|
||||||
let padding_border_sums = LogicalVec2 {
|
padding_border_sums = LogicalVec2 {
|
||||||
inline: (padding.inline_sum() + border.inline_sum() + margin.inline_sum())
|
inline: (padding.inline_sum() + border.inline_sum() + margin.inline_sum())
|
||||||
.into(),
|
.into(),
|
||||||
block: (padding.block_sum() + border.block_sum() + margin.block_sum()).into(),
|
block: (padding.block_sum() + border.block_sum() + margin.block_sum())
|
||||||
|
.into(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let (size, min_size, max_size) =
|
(size, min_size, max_size) = get_outer_sizes_from_style(
|
||||||
get_outer_sizes_from_style(&caption.style, writing_mode, &padding_border_sums);
|
&context.style,
|
||||||
let mut inline_content_sizes = caption
|
writing_mode,
|
||||||
.contents
|
&padding_border_sums,
|
||||||
.contents
|
);
|
||||||
.inline_content_sizes(layout_context, writing_mode);
|
size_is_auto = context.style.box_size(writing_mode).inline.is_auto();
|
||||||
|
}
|
||||||
|
|
||||||
inline_content_sizes.min_content += padding_border_sums.inline;
|
// If an inline size is defined it should serve as the upper limit and lower limit
|
||||||
inline_content_sizes
|
// of the caption inline size.
|
||||||
.min_content
|
let inline_size = if !size_is_auto {
|
||||||
.min(max_size.inline)
|
size.inline
|
||||||
.max(size.inline)
|
} else {
|
||||||
.max(min_size.inline)
|
let inline_content_sizes = caption
|
||||||
|
.context
|
||||||
|
.borrow_mut()
|
||||||
|
.inline_content_sizes(layout_context);
|
||||||
|
inline_content_sizes.min_content + padding_border_sums.inline
|
||||||
|
};
|
||||||
|
|
||||||
|
inline_size.min(max_size.inline).max(min_size.inline)
|
||||||
})
|
})
|
||||||
.max()
|
.max()
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
|
@ -761,7 +780,7 @@ impl<'a> TableLayout<'a> {
|
||||||
// > The assignable table width is the used width of the table minus the total horizontal
|
// > The assignable table width is the used width of the table minus the total horizontal
|
||||||
// > border spacing (if any). This is the width that we will be able to allocate to the
|
// > border spacing (if any). This is the width that we will be able to allocate to the
|
||||||
// > columns.
|
// > columns.
|
||||||
self.assignable_width = used_width_of_table - self.table.total_border_spacing().inline;
|
self.assignable_width = self.table_width - self.table.total_border_spacing().inline;
|
||||||
|
|
||||||
// This is the amount that we will use to resolve percentages in the padding of cells.
|
// This is the amount that we will use to resolve percentages in the padding of cells.
|
||||||
// It matches what Gecko and Blink do, though they disagree when there is a big caption.
|
// It matches what Gecko and Blink do, though they disagree when there is a big caption.
|
||||||
|
@ -1503,71 +1522,33 @@ impl<'a> TableLayout<'a> {
|
||||||
containing_block: &ContainingBlock,
|
containing_block: &ContainingBlock,
|
||||||
parent_positioning_context: &mut PositioningContext,
|
parent_positioning_context: &mut PositioningContext,
|
||||||
) -> BoxFragment {
|
) -> BoxFragment {
|
||||||
let pbm = caption.style.padding_border_margin(containing_block);
|
let context = caption.context.borrow();
|
||||||
let box_size = caption.style.content_box_size(containing_block, &pbm);
|
let mut positioning_context = PositioningContext::new_for_style(&context.style);
|
||||||
let max_box_size = caption.style.content_max_box_size(containing_block, &pbm);
|
let containing_block = &ContainingBlock {
|
||||||
let min_box_size = caption
|
inline_size: self.table_width + table_pbm.padding_border_sums.inline,
|
||||||
.style
|
block_size: AuOrAuto::Auto,
|
||||||
.content_min_box_size(containing_block, &pbm)
|
style: containing_block.style,
|
||||||
.auto_is(Length::zero);
|
|
||||||
let block_size = box_size.block.map(|block_size| {
|
|
||||||
block_size.clamp_between_extremums(min_box_size.block, max_box_size.block)
|
|
||||||
});
|
|
||||||
|
|
||||||
let table_inline_content_size = self.table_width - pbm.padding_border_sums.inline +
|
|
||||||
table_pbm.padding_border_sums.inline;
|
|
||||||
let inline_size = box_size
|
|
||||||
.inline
|
|
||||||
.auto_is(|| table_inline_content_size.into())
|
|
||||||
.clamp_between_extremums(min_box_size.inline, max_box_size.inline);
|
|
||||||
|
|
||||||
let containing_block_for_children = &ContainingBlock {
|
|
||||||
inline_size: inline_size.into(),
|
|
||||||
block_size: block_size.map(|t| t.into()),
|
|
||||||
style: &caption.style,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut positioning_context = PositioningContext::new_for_style(&caption.style);
|
let mut box_fragment = context.layout_in_flow_block_level(
|
||||||
let layout = caption.contents.layout(
|
|
||||||
layout_context,
|
layout_context,
|
||||||
positioning_context
|
positioning_context
|
||||||
.as_mut()
|
.as_mut()
|
||||||
.unwrap_or(parent_positioning_context),
|
.unwrap_or(parent_positioning_context),
|
||||||
containing_block_for_children,
|
containing_block,
|
||||||
|
None, /* sequential_layout_state */
|
||||||
);
|
);
|
||||||
|
|
||||||
|
box_fragment.content_rect.start_corner.block += box_fragment
|
||||||
|
.block_margins_collapsed_with_children
|
||||||
|
.start
|
||||||
|
.solve();
|
||||||
|
|
||||||
if let Some(positioning_context) = positioning_context.take() {
|
if let Some(positioning_context) = positioning_context.take() {
|
||||||
parent_positioning_context.append(positioning_context);
|
parent_positioning_context.append(positioning_context);
|
||||||
}
|
}
|
||||||
|
|
||||||
let ResolvedMargins {
|
box_fragment
|
||||||
margin,
|
|
||||||
effective_margin_inline_start,
|
|
||||||
} = solve_margins(containing_block, &pbm, containing_block.inline_size);
|
|
||||||
let content_rect = LogicalRect {
|
|
||||||
start_corner: LogicalVec2 {
|
|
||||||
inline: effective_margin_inline_start +
|
|
||||||
pbm.border.inline_start +
|
|
||||||
pbm.padding.inline_start,
|
|
||||||
block: margin.block_start + pbm.border.block_start + pbm.padding.block_start,
|
|
||||||
},
|
|
||||||
size: LogicalVec2 {
|
|
||||||
inline: inline_size.into(),
|
|
||||||
block: layout.content_block_size,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
BoxFragment::new(
|
|
||||||
caption.base_fragment_info,
|
|
||||||
caption.style.clone(),
|
|
||||||
layout.fragments,
|
|
||||||
content_rect,
|
|
||||||
pbm.padding,
|
|
||||||
pbm.border,
|
|
||||||
margin,
|
|
||||||
None, /* clearance */
|
|
||||||
CollapsedBlockMargins::zero(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Lay out the table (grid and captions) of this [`TableLayout`] into fragments. This should
|
/// Lay out the table (grid and captions) of this [`TableLayout`] into fragments. This should
|
||||||
|
@ -1616,7 +1597,7 @@ impl<'a> TableLayout<'a> {
|
||||||
table_layout
|
table_layout
|
||||||
.fragments
|
.fragments
|
||||||
.extend(self.table.captions.iter().filter_map(|caption| {
|
.extend(self.table.captions.iter().filter_map(|caption| {
|
||||||
if caption.style.clone_caption_side() != CaptionSide::Top {
|
if caption.context.borrow().style.clone_caption_side() != CaptionSide::Top {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1672,7 +1653,7 @@ impl<'a> TableLayout<'a> {
|
||||||
table_layout
|
table_layout
|
||||||
.fragments
|
.fragments
|
||||||
.extend(self.table.captions.iter().filter_map(|caption| {
|
.extend(self.table.captions.iter().filter_map(|caption| {
|
||||||
if caption.style.clone_caption_side() != CaptionSide::Bottom {
|
if caption.context.borrow().style.clone_caption_side() != CaptionSide::Bottom {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -79,7 +79,9 @@ use style::properties::ComputedValues;
|
||||||
use style_traits::dom::OpaqueNode;
|
use style_traits::dom::OpaqueNode;
|
||||||
|
|
||||||
use super::flow::BlockFormattingContext;
|
use super::flow::BlockFormattingContext;
|
||||||
|
use crate::cell::ArcRefCell;
|
||||||
use crate::flow::BlockContainer;
|
use crate::flow::BlockContainer;
|
||||||
|
use crate::formatting_contexts::NonReplacedFormattingContext;
|
||||||
use crate::fragment_tree::BaseFragmentInfo;
|
use crate::fragment_tree::BaseFragmentInfo;
|
||||||
|
|
||||||
pub type TableSize = Size2D<usize, UnknownUnit>;
|
pub type TableSize = Size2D<usize, UnknownUnit>;
|
||||||
|
@ -316,12 +318,5 @@ impl TableTrackGroup {
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
pub struct TableCaption {
|
pub struct TableCaption {
|
||||||
/// The contents of this cell, with its own layout.
|
/// The contents of this cell, with its own layout.
|
||||||
contents: BlockFormattingContext,
|
context: ArcRefCell<NonReplacedFormattingContext>,
|
||||||
|
|
||||||
/// The style of this table cell.
|
|
||||||
#[serde(skip_serializing)]
|
|
||||||
style: Arc<ComputedValues>,
|
|
||||||
|
|
||||||
/// The [`BaseFragmentInfo`] of this cell.
|
|
||||||
base_fragment_info: BaseFragmentInfo,
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
[table-caption-passes-abspos-up-001.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[background-applies-to-015.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[background-attachment-applies-to-015.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[background-color-applies-to-015.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[background-image-applies-to-015.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[background-position-applies-to-015.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[background-repeat-applies-to-015.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[border-applies-to-015.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[border-color-applies-to-015.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[border-left-applies-to-015.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[border-left-color-applies-to-015.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[border-left-width-applies-to-015.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[border-right-applies-to-015.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[border-right-color-applies-to-015.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[border-right-width-applies-to-015.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[border-width-applies-to-015.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[clear-applies-to-015.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[vertical-align-applies-to-015.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[margin-applies-to-015.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[margin-left-applies-to-015.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[margin-right-applies-to-015.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[block-formatting-contexts-012.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[height-applies-to-015.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[max-height-applies-to-015.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[max-width-applies-to-015.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[min-height-applies-to-015.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[min-width-applies-to-015.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[width-applies-to-015.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[clear-applies-to-016.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[clear-applies-to-017.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -26,8 +26,5 @@
|
||||||
[.target > * 18]
|
[.target > * 18]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[.target > * 19]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[.target > * 21]
|
[.target > * 21]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
|
@ -32,8 +32,5 @@
|
||||||
[.target > * 18]
|
[.target > * 18]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[.target > * 19]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[.target > * 21]
|
[.target > * 21]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
|
@ -1,15 +1,3 @@
|
||||||
[caption.html]
|
[caption.html]
|
||||||
[table 1]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[table 2]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[table 3]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[table 12]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[table 13]
|
[table 13]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
|
@ -1,28 +1,10 @@
|
||||||
[column-widths.html]
|
[column-widths.html]
|
||||||
[table 1]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[table 3]
|
[table 3]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[table 5]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[table 8]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[table 11]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[table 13]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[table 14]
|
[table 14]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[table 17]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[table 19]
|
[table 19]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -49,6 +31,3 @@
|
||||||
|
|
||||||
[table 33]
|
[table 33]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[table 4]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -4,3 +4,6 @@
|
||||||
|
|
||||||
[table 3]
|
[table 3]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
[table 6]
|
||||||
|
expected: FAIL
|
||||||
|
|
|
@ -2,9 +2,6 @@
|
||||||
[table 1]
|
[table 1]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[table 2]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[table 4]
|
[table 4]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
|
|
@ -5,9 +5,6 @@
|
||||||
[table 4]
|
[table 4]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[table 5]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[table 7]
|
[table 7]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
|
|
@ -1,43 +1,13 @@
|
||||||
[table-width-redistribution.html]
|
[table-width-redistribution.html]
|
||||||
[table 1]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[table 2]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[table 3]
|
[table 3]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[table 4]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[table 5]
|
[table 5]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[table 6]
|
[table 6]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[table 9]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[table 10]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[table 11]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[table 12]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[table 13]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[table 14]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[table 15]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[table 20]
|
[table 20]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
[computing-table-width-0.html]
|
|
||||||
[Width is 100px due to 100px content size of caption]
|
|
||||||
expected: FAIL
|
|
|
@ -1,27 +1,6 @@
|
||||||
[table-client-props.html]
|
[table-client-props.html]
|
||||||
[Caption with margin]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Table with separated border]
|
[Table with separated border]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[Table with collapsed border]
|
[Table with collapsed border]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[Table and wider caption]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Table and narrower caption]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Basic caption]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Caption with padding]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Caption with border]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Bottom caption]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -1,24 +1,3 @@
|
||||||
[table-offset-props.html]
|
[table-offset-props.html]
|
||||||
[Caption with margin]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Table with collapsed border]
|
[Table with collapsed border]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[Table and wider caption]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Table and narrower caption]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Basic caption]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Caption with padding]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Caption with border]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Bottom caption]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -1,24 +1,3 @@
|
||||||
[table-scroll-props.html]
|
[table-scroll-props.html]
|
||||||
[Caption with margin]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Table with collapsed border]
|
[Table with collapsed border]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[Table and wider caption]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Table and narrower caption]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Basic caption]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Caption with padding]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Caption with border]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[Bottom caption]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -14,27 +14,12 @@
|
||||||
[display: inline - available width: 300px]
|
[display: inline - available width: 300px]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[display: inline-block - available width: 50px]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[display: inline-block - available width: 200px]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[display: list-item - available width: 50px]
|
[display: list-item - available width: 50px]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[display: list-item - available width: 300px]
|
[display: list-item - available width: 300px]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[display: table - available width: 50px]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[display: table-row - available width: 50px]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[display: table-cell - available width: 50px]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[float: left - available width: 50px]
|
[float: left - available width: 50px]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
@ -43,12 +28,3 @@
|
||||||
|
|
||||||
[float: left - available width: 300px]
|
[float: left - available width: 300px]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[display: table - available width: 200px]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[display: table-row - available width: 200px]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[display: table-cell - available width: 200px]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
[table-cell-width-calculation-applies-to.html]
|
|
||||||
[table 1]
|
|
||||||
expected: FAIL
|
|
|
@ -11,29 +11,8 @@
|
||||||
[The table cell width calculation quirk, the don't-wrap rule is only for the purpose of calculating the width of the cell]
|
[The table cell width calculation quirk, the don't-wrap rule is only for the purpose of calculating the width of the cell]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[The table cell width calculation quirk, the quirk only applies when the cell is the containing block]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[The table cell width calculation quirk, zero width on cell, specified with on table]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[The table cell width calculation quirk, display:table-cell on span]
|
[The table cell width calculation quirk, display:table-cell on span]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[The table cell width calculation quirk, display:table-cell on span, wbr]
|
[The table cell width calculation quirk, display:table-cell on span, wbr]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
[The table cell width calculation quirk, the quirk shouldn't apply for generated content]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[The table cell width calculation quirk, the quirk shouldn't apply for <input>]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[The table cell width calculation quirk, the quirk shouldn't apply for <video poster>]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[The table cell width calculation quirk, non-auto width on cell]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[The table cell width calculation quirk, the quirk shouldn't apply for <object>]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue