Commit ed553796 authored by Koji Ishii's avatar Koji Ishii Committed by Commit Bot

[LayoutNG] Implement inline fragmentation for BiDi

This patch impements inline fragmentation for BiDi.

When BiDi reorder fragments an inline box; e.g.,
  <span>T1 T2</span>T3
becomes after BiDi reorder:
  <span>T1</span> T3 <span>T2</span>
this change creates two box fragments, one for T1 and another
for T2.

Unfortunately, one test starts failing:
fast/css/css-properties-position-relative-as-parent-fixed.html

In this test, when absolute/fixed positioned container is
fragmented, the position becomes incorrect. As far as I
checked, the fragment tree looks correct, so I suppose
computing positioned objects get confused by multiple
fragments for a LayoutObject within a line.

Bug: 636993
Change-Id: Id1055e411e7cb59a8c8e731e5ad7c74b0c7fd246
Reviewed-on: https://chromium-review.googlesource.com/c/1323989
Commit-Queue: Koji Ishii <kojii@chromium.org>
Reviewed-by: default avatarEmil A Eklund <eae@chromium.org>
Cr-Commit-Position: refs/heads/master@{#607412}
parent b05de7e5
...@@ -258,12 +258,11 @@ crbug.com/591099 external/wpt/workers/constructors/Worker/same-origin.html [ Pas ...@@ -258,12 +258,11 @@ crbug.com/591099 external/wpt/workers/constructors/Worker/same-origin.html [ Pas
crbug.com/591099 fast/backgrounds/quirks-mode-line-box-backgrounds.html [ Failure ] crbug.com/591099 fast/backgrounds/quirks-mode-line-box-backgrounds.html [ Failure ]
crbug.com/591099 fast/block/float-avoids-padding-inline-ancestors.html [ Failure ] crbug.com/591099 fast/block/float-avoids-padding-inline-ancestors.html [ Failure ]
crbug.com/591099 fast/block/positioning/positioned-child-inside-relative-positioned-anonymous-block.html [ Failure ] crbug.com/591099 fast/block/positioning/positioned-child-inside-relative-positioned-anonymous-block.html [ Failure ]
crbug.com/591099 fast/borders/bidi-002.html [ Failure ]
crbug.com/859497 fast/borders/bidi-009a.html [ Failure ]
crbug.com/591099 fast/borders/inline-mask-overlay-image-outset-vertical-rl.html [ Failure ] crbug.com/591099 fast/borders/inline-mask-overlay-image-outset-vertical-rl.html [ Failure ]
crbug.com/591099 fast/css-intrinsic-dimensions/height-positioned.html [ Pass ] crbug.com/591099 fast/css-intrinsic-dimensions/height-positioned.html [ Pass ]
crbug.com/807708 fast/css-intrinsic-dimensions/width-avoid-floats.html [ Failure ] crbug.com/807708 fast/css-intrinsic-dimensions/width-avoid-floats.html [ Failure ]
crbug.com/591099 fast/css/absolute-inline-alignment-2.html [ Pass ] crbug.com/591099 fast/css/absolute-inline-alignment-2.html [ Pass ]
crbug.com/591099 fast/css/css-properties-position-relative-as-parent-fixed.html [ Failure ]
crbug.com/835484 fast/css/focus-ring-recursive-continuations.html [ Failure ] crbug.com/835484 fast/css/focus-ring-recursive-continuations.html [ Failure ]
crbug.com/835484 fast/css/outline-narrowLine.html [ Failure ] crbug.com/835484 fast/css/outline-narrowLine.html [ Failure ]
crbug.com/591099 fast/css3-text/css3-text-decoration/text-underline-position/text-underline-position-under.html [ Failure ] crbug.com/591099 fast/css3-text/css3-text-decoration/text-underline-position/text-underline-position-under.html [ Failure ]
......
...@@ -289,9 +289,8 @@ void NGInlineLayoutStateStack::AddBoxFragmentPlaceholder( ...@@ -289,9 +289,8 @@ void NGInlineLayoutStateStack::AddBoxFragmentPlaceholder(
unsigned fragment_end = line_box->size(); unsigned fragment_end = line_box->size();
DCHECK(box->item); DCHECK(box->item);
box_data_list_.push_back( BoxData& box_data = box_data_list_.emplace_back(
BoxData{box->fragment_start, fragment_end, box->item, size}); box->fragment_start, fragment_end, box->item, size);
BoxData& box_data = box_data_list_.back();
box_data.padding = box->padding; box_data.padding = box->padding;
box_data.inline_container = box->inline_container; box_data.inline_container = box->inline_container;
if (box->has_start_edge) { if (box->has_start_edge) {
...@@ -344,6 +343,10 @@ void NGInlineLayoutStateStack::AddBoxFragmentPlaceholder( ...@@ -344,6 +343,10 @@ void NGInlineLayoutStateStack::AddBoxFragmentPlaceholder(
void NGInlineLayoutStateStack::PrepareForReorder( void NGInlineLayoutStateStack::PrepareForReorder(
NGLineBoxFragmentBuilder::ChildList* line_box) { NGLineBoxFragmentBuilder::ChildList* line_box) {
// There's nothing to do if no boxes.
if (box_data_list_.IsEmpty())
return;
// Set indexes of BoxData to the children of the line box. // Set indexes of BoxData to the children of the line box.
unsigned box_data_index = 0; unsigned box_data_index = 0;
for (const BoxData& box_data : box_data_list_) { for (const BoxData& box_data : box_data_list_) {
...@@ -361,42 +364,29 @@ void NGInlineLayoutStateStack::PrepareForReorder( ...@@ -361,42 +364,29 @@ void NGInlineLayoutStateStack::PrepareForReorder(
const NGLineBoxFragmentBuilder::Child& placeholder = const NGLineBoxFragmentBuilder::Child& placeholder =
(*line_box)[box_data.fragment_end]; (*line_box)[box_data.fragment_end];
DCHECK(!placeholder.HasFragment()); DCHECK(!placeholder.HasFragment());
box_data.box_data_index = placeholder.box_data_index; box_data.parent_box_data_index = placeholder.box_data_index;
} }
} }
void NGInlineLayoutStateStack::UpdateAfterReorder( void NGInlineLayoutStateStack::UpdateAfterReorder(
NGLineBoxFragmentBuilder::ChildList* line_box) { NGLineBoxFragmentBuilder::ChildList* line_box) {
// There's nothing to do if no boxes.
if (box_data_list_.IsEmpty())
return;
// Compute start/end of boxes from the children of the line box. // Compute start/end of boxes from the children of the line box.
// Clear start/end first.
for (BoxData& box_data : box_data_list_) for (BoxData& box_data : box_data_list_)
box_data.fragment_start = box_data.fragment_end = 0; box_data.fragment_start = box_data.fragment_end = 0;
for (unsigned i = 0; i < line_box->size(); i++) {
const NGLineBoxFragmentBuilder::Child& child = (*line_box)[i];
if (child.IsPlaceholder())
continue;
if (unsigned box_data_index = child.box_data_index) {
BoxData& box_data = box_data_list_[box_data_index - 1];
if (!box_data.fragment_end)
box_data.fragment_start = i;
box_data.fragment_end = i + 1;
}
}
// Extend start/end of boxes when they are nested. // Scan children and update start/end from their box_data_index.
for (BoxData& box_data : box_data_list_) { unsigned box_count = box_data_list_.size();
if (box_data.box_data_index) { for (unsigned index = 0; index < line_box->size();)
BoxData& parent_box_data = box_data_list_[box_data.box_data_index - 1]; index = UpdateBoxDataFragmentRange(line_box, index);
if (!parent_box_data.fragment_end) {
parent_box_data.fragment_start = box_data.fragment_start; // If any inline fragmentation due to BiDi reorder, adjust box edges.
parent_box_data.fragment_end = box_data.fragment_end; if (box_count != box_data_list_.size())
} else { UpdateFragmentedBoxDataEdges();
parent_box_data.fragment_start =
std::min(box_data.fragment_start, parent_box_data.fragment_start);
parent_box_data.fragment_end =
std::max(box_data.fragment_end, parent_box_data.fragment_end);
}
}
}
#if DCHECK_IS_ON() #if DCHECK_IS_ON()
// Check all BoxData have ranges. // Check all BoxData have ranges.
...@@ -404,9 +394,95 @@ void NGInlineLayoutStateStack::UpdateAfterReorder( ...@@ -404,9 +394,95 @@ void NGInlineLayoutStateStack::UpdateAfterReorder(
DCHECK_NE(box_data.fragment_end, 0u); DCHECK_NE(box_data.fragment_end, 0u);
DCHECK_GT(box_data.fragment_end, box_data.fragment_start); DCHECK_GT(box_data.fragment_end, box_data.fragment_start);
} }
// Check all |box_data_index| were migrated to BoxData.
for (const NGLineBoxFragmentBuilder::Child& child : *line_box) {
DCHECK_EQ(child.box_data_index, 0u);
}
#endif #endif
} }
unsigned NGInlineLayoutStateStack::UpdateBoxDataFragmentRange(
NGLineBoxFragmentBuilder::ChildList* line_box,
unsigned index) {
// Find the first line box item that should create a box fragment.
for (; index < line_box->size(); index++) {
NGLineBoxFragmentBuilder::Child* start = &(*line_box)[index];
if (start->IsPlaceholder())
continue;
const unsigned box_data_index = start->box_data_index;
if (!box_data_index)
continue;
// As |box_data_index| is converted to start/end of BoxData, update
// |box_data_index| to the parent box, or to 0 if no parent boxes.
// This allows including this box to the nested parent box.
BoxData* box_data = &box_data_list_[box_data_index - 1];
start->box_data_index = box_data->parent_box_data_index;
// Find the end line box item.
const unsigned start_index = index;
for (index++; index < line_box->size(); index++) {
NGLineBoxFragmentBuilder::Child* end = &(*line_box)[index];
if (end->IsPlaceholder())
continue;
// If we found another box that maybe included in this box, update it
// first. Updating will change |end->box_data_index| so that we can
// determine if it should be included into this box or not.
// It also changes other BoxData, but not the one we're dealing with here
// because the update is limited only when its |box_data_index| is lower.
while (end->box_data_index && end->box_data_index < box_data_index) {
UpdateBoxDataFragmentRange(line_box, index);
// Re-compute |box_data| in case |box_data_list_| was reallocated when
// |UpdateBoxDataFragmentRange| added new fragments.
box_data = &box_data_list_[box_data_index - 1];
}
if (box_data_index != end->box_data_index)
break;
end->box_data_index = box_data->parent_box_data_index;
}
// If this is the first range for this BoxData, set it.
if (!box_data->fragment_end) {
box_data->fragment_start = start_index;
box_data->fragment_end = index;
} else {
// This box is fragmented by BiDi reordering. Add a new BoxData for the
// fragmented range.
box_data->fragmented_box_data_index = box_data_list_.size();
box_data_list_.emplace_back(*box_data, start_index, index);
}
return box_data->parent_box_data_index ? start_index : index;
}
return index;
}
void NGInlineLayoutStateStack::UpdateFragmentedBoxDataEdges() {
for (BoxData& box_data : box_data_list_) {
if (box_data.fragmented_box_data_index)
box_data.UpdateFragmentEdges(box_data_list_);
}
}
void NGInlineLayoutStateStack::BoxData::UpdateFragmentEdges(
Vector<BoxData, 4>& list) {
DCHECK(fragmented_box_data_index);
// If this box has the right edge, move it to the last fragment.
if (has_line_right_edge) {
BoxData& last = list[fragmented_box_data_index];
last.has_line_right_edge = true;
last.margin_line_right = margin_line_right;
last.margin_border_padding_line_right = margin_border_padding_line_right;
last.padding.inline_end = padding.inline_end;
has_line_right_edge = false;
margin_line_right = margin_border_padding_line_right = padding.inline_end =
LayoutUnit();
}
}
LayoutUnit NGInlineLayoutStateStack::ComputeInlinePositions( LayoutUnit NGInlineLayoutStateStack::ComputeInlinePositions(
NGLineBoxFragmentBuilder::ChildList* line_box) { NGLineBoxFragmentBuilder::ChildList* line_box) {
// At this point, children are in the visual order, and they have their // At this point, children are in the visual order, and they have their
......
...@@ -148,6 +148,18 @@ class CORE_EXPORT NGInlineLayoutStateStack { ...@@ -148,6 +148,18 @@ class CORE_EXPORT NGInlineLayoutStateStack {
// reordering. // reordering.
void UpdateAfterReorder(NGLineBoxFragmentBuilder::ChildList*); void UpdateAfterReorder(NGLineBoxFragmentBuilder::ChildList*);
// Update start/end of the first BoxData found at |index|.
//
// If inline fragmentation is found, a new BoxData is added.
//
// Returns the index to process next. It should be given to the next call to
// this function.
unsigned UpdateBoxDataFragmentRange(NGLineBoxFragmentBuilder::ChildList*,
unsigned index);
// Update edges of inline fragmented boxes.
void UpdateFragmentedBoxDataEdges();
// Compute inline positions of fragments and boxes. // Compute inline positions of fragments and boxes.
LayoutUnit ComputeInlinePositions(NGLineBoxFragmentBuilder::ChildList*); LayoutUnit ComputeInlinePositions(NGLineBoxFragmentBuilder::ChildList*);
...@@ -191,8 +203,24 @@ class CORE_EXPORT NGInlineLayoutStateStack { ...@@ -191,8 +203,24 @@ class CORE_EXPORT NGInlineLayoutStateStack {
// Data for a box fragment. See AddBoxFragmentPlaceholder(). // Data for a box fragment. See AddBoxFragmentPlaceholder().
// This is a transient object only while building a line box. // This is a transient object only while building a line box.
struct BoxData { struct BoxData {
BoxData(unsigned start,
unsigned end,
const NGInlineItem* item,
NGLogicalSize size)
: fragment_start(start), fragment_end(end), item(item), size(size) {}
BoxData(const BoxData& other, unsigned start, unsigned end)
: fragment_start(start),
fragment_end(end),
item(other.item),
size(other.size),
inline_container(other.inline_container),
offset(other.offset) {}
// The range of child fragments this box contains.
unsigned fragment_start; unsigned fragment_start;
unsigned fragment_end; unsigned fragment_end;
const NGInlineItem* item; const NGInlineItem* item;
NGLogicalSize size; NGLogicalSize size;
...@@ -207,7 +235,10 @@ class CORE_EXPORT NGInlineLayoutStateStack { ...@@ -207,7 +235,10 @@ class CORE_EXPORT NGInlineLayoutStateStack {
LayoutUnit margin_border_padding_line_right; LayoutUnit margin_border_padding_line_right;
NGLogicalOffset offset; NGLogicalOffset offset;
unsigned box_data_index = 0; unsigned parent_box_data_index = 0;
unsigned fragmented_box_data_index = 0;
void UpdateFragmentEdges(Vector<BoxData, 4>& list);
scoped_refptr<NGLayoutResult> CreateBoxFragment( scoped_refptr<NGLayoutResult> CreateBoxFragment(
NGLineBoxFragmentBuilder::ChildList*); NGLineBoxFragmentBuilder::ChildList*);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment