diff --git a/net/quic/chromium/quic_chromium_client_stream.cc b/net/quic/chromium/quic_chromium_client_stream.cc index 1b1c90e..a084fe5c 100644 --- a/net/quic/chromium/quic_chromium_client_stream.cc +++ b/net/quic/chromium/quic_chromium_client_stream.cc
@@ -73,6 +73,13 @@ ResetAndReturn(&read_body_callback_).Run(rv); } +void QuicChromiumClientStream::Handle::OnCanWrite() { + if (!write_callback_) + return; + + base::ResetAndReturn(&write_callback_).Run(OK); +} + void QuicChromiumClientStream::Handle::OnClose() { if (stream_) SaveState(); @@ -144,7 +151,12 @@ const CompletionCallback& callback) { if (!stream_) return ERR_CONNECTION_CLOSED; - return stream_->WriteStreamData(data, fin, callback); + + if (stream_->WriteStreamData(data, fin)) + return OK; + + write_callback_ = callback; + return ERR_IO_PENDING; } int QuicChromiumClientStream::Handle::WritevStreamData( @@ -154,7 +166,12 @@ const CompletionCallback& callback) { if (!stream_) return ERR_CONNECTION_CLOSED; - return stream_->WritevStreamData(buffers, lengths, fin, callback); + + if (stream_->WritevStreamData(buffers, lengths, fin)) + return OK; + + write_callback_ = callback; + return ERR_IO_PENDING; } int QuicChromiumClientStream::Handle::Read(IOBuffer* buf, int buf_len) { @@ -386,9 +403,8 @@ void QuicChromiumClientStream::OnCanWrite() { QuicStream::OnCanWrite(); - if (!HasBufferedData() && !write_callback_.is_null()) { - base::ResetAndReturn(&write_callback_).Run(OK); - } + if (!HasBufferedData() && handle_) + handle_->OnCanWrite(); } size_t QuicChromiumClientStream::WriteHeaders( @@ -415,27 +431,18 @@ : kV3HighestPriority; } -int QuicChromiumClientStream::WriteStreamData( - QuicStringPiece data, - bool fin, - const CompletionCallback& callback) { - // We should not have data buffered. +bool QuicChromiumClientStream::WriteStreamData(QuicStringPiece data, bool fin) { + // Must not be called when data is buffered. DCHECK(!HasBufferedData()); // Writes the data, or buffers it. WriteOrBufferData(data, fin, nullptr); - if (!HasBufferedData()) { - return OK; - } - - write_callback_ = callback; - return ERR_IO_PENDING; + return !HasBufferedData(); // Was all data written? } -int QuicChromiumClientStream::WritevStreamData( +bool QuicChromiumClientStream::WritevStreamData( const std::vector<scoped_refptr<IOBuffer>>& buffers, const std::vector<int>& lengths, - bool fin, - const CompletionCallback& callback) { + bool fin) { // Must not be called when data is buffered. DCHECK(!HasBufferedData()); // Writes the data, or buffers it. @@ -444,12 +451,7 @@ QuicStringPiece string_data(buffers[i]->data(), lengths[i]); WriteOrBufferData(string_data, is_fin, nullptr); } - if (!HasBufferedData()) { - return OK; - } - - write_callback_ = callback; - return ERR_IO_PENDING; + return !HasBufferedData(); // Was all data written? } std::unique_ptr<QuicChromiumClientStream::Handle>
diff --git a/net/quic/chromium/quic_chromium_client_stream.h b/net/quic/chromium/quic_chromium_client_stream.h index 14901d8edc..e512b457 100644 --- a/net/quic/chromium/quic_chromium_client_stream.h +++ b/net/quic/chromium/quic_chromium_client_stream.h
@@ -157,6 +157,7 @@ void OnTrailingHeadersAvailable(const SpdyHeaderBlock& headers, size_t frame_len); void OnDataAvailable(); + void OnCanWrite(); void OnClose(); void OnError(int error); @@ -166,13 +167,19 @@ QuicChromiumClientStream* stream_; // Unowned. Delegate* delegate_; // Owns this. + // Callback to be invoked when ReadHeaders completes asynchronously. CompletionCallback read_headers_callback_; SpdyHeaderBlock* read_headers_buffer_; + // Callback to be invoked when ReadBody completes asynchronously. CompletionCallback read_body_callback_; IOBuffer* read_body_buffer_; int read_body_buffer_len_; + // Callback to be invoked when WriteStreamData or WritevStreamData completes + // asynchronously. + CompletionCallback write_callback_; + QuicStreamId id_; QuicErrorCode connection_error_; QuicRstStreamErrorCode stream_error_; @@ -217,15 +224,15 @@ // of client-side streams should be able to set the priority. using QuicSpdyStream::SetPriority; - int WriteStreamData(QuicStringPiece data, - bool fin, - const CompletionCallback& callback); + // Writes |data| to the peer and closes the write side if |fin| is true. + // Returns true if the data have been fully written. If the data was not fully + // written, returns false and OnCanWrite() will be invoked later. + bool WriteStreamData(QuicStringPiece data, bool fin); // Same as WriteStreamData except it writes data from a vector of IOBuffers, // with the length of each buffer at the corresponding index in |lengths|. - int WritevStreamData(const std::vector<scoped_refptr<IOBuffer>>& buffers, - const std::vector<int>& lengths, - bool fin, - const CompletionCallback& callback); + bool WritevStreamData(const std::vector<scoped_refptr<IOBuffer>>& buffers, + const std::vector<int>& lengths, + bool fin); // Creates a new Handle for this stream and sets |delegate| on the handle. // Must only be called once. @@ -274,10 +281,6 @@ // True when initial headers have been sent. bool initial_headers_sent_; - // Callback to be invoked when WriteStreamData or WritevStreamData completes - // asynchronously. - CompletionCallback write_callback_; - QuicClientSessionBase* session_; // Set to false if this stream to not be migrated during connection migration.
diff --git a/net/quic/chromium/quic_chromium_client_stream_test.cc b/net/quic/chromium/quic_chromium_client_stream_test.cc index aba5b919..0623610f 100644 --- a/net/quic/chromium/quic_chromium_client_stream_test.cc +++ b/net/quic/chromium/quic_chromium_client_stream_test.cc
@@ -641,7 +641,7 @@ EXPECT_CALL(session_, WritevData(stream_, stream_->id(), _, _, _, _)) .WillOnce(Return(QuicConsumedData(kDataLen, true))); TestCompletionCallback callback; - EXPECT_EQ(OK, stream_->WriteStreamData(QuicStringPiece(kData1, kDataLen), + EXPECT_EQ(OK, handle_->WriteStreamData(QuicStringPiece(kData1, kDataLen), true, callback.callback())); } @@ -657,7 +657,7 @@ .WillOnce(Return(QuicConsumedData(0, false))); TestCompletionCallback callback; EXPECT_EQ(ERR_IO_PENDING, - stream_->WriteStreamData(QuicStringPiece(kData1, kDataLen), true, + handle_->WriteStreamData(QuicStringPiece(kData1, kDataLen), true, callback.callback())); ASSERT_FALSE(callback.have_result()); @@ -682,7 +682,7 @@ .WillOnce(Return(QuicConsumedData(buf2->size(), true))); TestCompletionCallback callback; EXPECT_EQ( - OK, stream_->WritevStreamData({buf1, buf2}, {buf1->size(), buf2->size()}, + OK, handle_->WritevStreamData({buf1, buf2}, {buf1->size(), buf2->size()}, true, callback.callback())); } @@ -702,7 +702,7 @@ .WillOnce(Return(QuicConsumedData(0, false))); TestCompletionCallback callback; EXPECT_EQ(ERR_IO_PENDING, - stream_->WritevStreamData({buf1.get(), buf2.get()}, + handle_->WritevStreamData({buf1.get(), buf2.get()}, {buf1->size(), buf2->size()}, true, callback.callback())); ASSERT_FALSE(callback.have_result());
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations index 553ca7d2..1d2c0df7 100644 --- a/third_party/WebKit/LayoutTests/TestExpectations +++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -859,6 +859,7 @@ crbug.com/635619 virtual/layout_ng/fast/block/float/canvas-with-floats-marked-for-layout.html [ Crash ] crbug.com/635619 virtual/layout_ng/fast/block/float/centered-float-avoidance-complexity.html [ Failure ] crbug.com/635619 virtual/layout_ng/fast/block/float/checkbox-and-radio-avoid-floats.html [ Failure ] +crbug.com/635619 virtual/layout_ng/fast/block/float/clamped-right-float.html [ Failure ] crbug.com/635619 virtual/layout_ng/fast/block/float/clear-element-too-wide-for-containing-block.html [ Failure ] crbug.com/635619 virtual/layout_ng/fast/block/float/clear-intruding-floats-when-moving-to-inline-parent-3.html [ Crash Failure ] crbug.com/635619 virtual/layout_ng/fast/block/float/clear-to-fit.html [ Failure ]
diff --git a/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_box_state.cc b/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_box_state.cc index 4d27613..64cdfd1 100644 --- a/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_box_state.cc +++ b/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_box_state.cc
@@ -7,6 +7,8 @@ #include "core/layout/ng/inline/ng_inline_node.h" #include "core/layout/ng/inline/ng_line_box_fragment_builder.h" #include "core/layout/ng/inline/ng_text_fragment_builder.h" +#include "core/layout/ng/ng_fragment_builder.h" +#include "core/layout/ng/ng_layout_result.h" #include "core/style/ComputedStyle.h" namespace blink { @@ -36,6 +38,16 @@ } } +void NGInlineBoxState::SetNeedsBoxFragment( + LayoutUnit position, + LayoutUnit borders_paddings_block_start, + LayoutUnit borders_paddings_block_height) { + needs_box_fragment = true; + line_left_position = position; + this->borders_paddings_block_start = borders_paddings_block_start; + this->borders_paddings_block_height = borders_paddings_block_height; +} + NGInlineBoxState* NGInlineLayoutStateStack::OnBeginPlaceItems( const ComputedStyle* line_style, FontBaseline baseline_type) { @@ -67,13 +79,12 @@ NGInlineBoxState* NGInlineLayoutStateStack::OnOpenTag( const NGInlineItem& item, - NGLineBoxFragmentBuilder* line_box, - NGTextFragmentBuilder* text_builder) { + NGLineBoxFragmentBuilder* line_box) { stack_.resize(stack_.size() + 1); NGInlineBoxState* box = &stack_.back(); box->fragment_start = line_box->Children().size(); + box->item = &item; box->style = item.Style(); - text_builder->SetDirection(box->style->Direction()); return box; } @@ -81,8 +92,9 @@ const NGInlineItem& item, NGLineBoxFragmentBuilder* line_box, NGInlineBoxState* box, - FontBaseline baseline_type) { - EndBoxState(box, line_box, baseline_type); + FontBaseline baseline_type, + LayoutUnit position) { + EndBoxState(box, line_box, baseline_type, position); // TODO(kojii): When the algorithm restarts from a break token, the stack may // underflow. We need either synthesize a missing box state, or push all // parents on initialize. @@ -92,19 +104,27 @@ void NGInlineLayoutStateStack::OnEndPlaceItems( NGLineBoxFragmentBuilder* line_box, - FontBaseline baseline_type) { + FontBaseline baseline_type, + LayoutUnit position) { for (auto it = stack_.rbegin(); it != stack_.rend(); ++it) { NGInlineBoxState* box = &(*it); - EndBoxState(box, line_box, baseline_type); + EndBoxState(box, line_box, baseline_type, position); } + if (!box_placeholders_.IsEmpty()) + CreateBoxFragments(line_box); + DCHECK(!LineBoxState().metrics.IsEmpty()); line_box->SetMetrics(LineBoxState().metrics); } void NGInlineLayoutStateStack::EndBoxState(NGInlineBoxState* box, NGLineBoxFragmentBuilder* line_box, - FontBaseline baseline_type) { + FontBaseline baseline_type, + LayoutUnit position) { + if (box->needs_box_fragment) + AddBoxFragmentPlaceholder(box, line_box, baseline_type, position); + PositionPending position_pending = ApplyBaselineShift(box, line_box, baseline_type); @@ -114,6 +134,106 @@ } } +// Crete a placeholder for a box fragment. +// We keep a flat list of fragments because it is more suitable for operations +// such as ApplyBaselineShift. Later, CreateBoxFragments() creates box fragments +// from placeholders. +void NGInlineLayoutStateStack::AddBoxFragmentPlaceholder( + NGInlineBoxState* box, + NGLineBoxFragmentBuilder* line_box, + FontBaseline baseline_type, + LayoutUnit position) { + if (box->fragment_start == line_box->Children().size()) { + // Don't create a placeholder if the inline box is empty. + // Whether to create this box or not affects layout when the line contains + // only this box, since this box participates the line height. + // TODO(kojii): Testing indicates that we should create a box if it has + // borders, but not for backgrounds. But creating such a RootInlineBox needs + // additional code. The plan is to enable such line box when NG paint is + // enabled. + return; + } + + // The inline box should have the height of the font metrics without the + // line-height property. Compute from style because |box->metrics| includes + // the line-height property. + DCHECK(box->style); + const ComputedStyle& style = *box->style; + NGLineHeightMetrics metrics(style, baseline_type); + + // Extend the block direction of the box by borders and paddings. Inline + // direction is already included into positions in NGLineBreaker. + NGLogicalRect bounds( + box->line_left_position, + -metrics.ascent - box->borders_paddings_block_start, + position - box->line_left_position, + metrics.LineHeight() + box->borders_paddings_block_height); + + // The start is marked only in BoxFragmentPlaceholder, while end is marked + // in both BoxFragmentPlaceholder and the list itself. + // With a list of 4 text fragments: + // | 0 | 1 | 2 | 3 | + // |text0|text1|text2|text3| + // By adding a BoxFragmentPlaceholder(2,4) (end is exclusive), it becomes: + // | 0 | 1 | 2 | 3 | 4 | + // |text0|text1|text2|text3|null | + // The "null" is added to the list to compute baseline shift of the box + // separately from text fragments. + unsigned fragment_end = line_box->Children().size(); + box_placeholders_.push_back(BoxFragmentPlaceholder{ + box->fragment_start, fragment_end, box->item, bounds.size}); + line_box->AddChild(nullptr, bounds.offset); +} + +// Create box fragments and construct a tree from the placeholders. +void NGInlineLayoutStateStack::CreateBoxFragments( + NGLineBoxFragmentBuilder* line_box) { + DCHECK(!box_placeholders_.IsEmpty()); + + Vector<RefPtr<NGPhysicalFragment>> children = + std::move(line_box->MutableChildren()); + Vector<NGLogicalOffset> offsets = std::move(line_box->MutableOffsets()); + DCHECK(line_box->Children().IsEmpty() && line_box->Offsets().IsEmpty()); + + // At this point, children is a list of text fragments and box placeholders. + // | 0 | 1 | 2 | 3 | 4 | 5 | + // |text0|text1|text2|text3|null1|text5| + // When there is a BoxFragmentPlaceholder(2,4), this loop creates a box + // fragment with text2 and text3 as its children and changes the list to: + // | 0 | 1 | 2 | 3 | 4 | 5 | + // |text0|text1|null |null | box |text5| + for (const BoxFragmentPlaceholder& placeholder : box_placeholders_) { + NGFragmentBuilder box(NGPhysicalFragment::kFragmentBox, + placeholder.item->GetLayoutObject()); + const NGLogicalOffset& box_offset = offsets[placeholder.fragment_end]; + for (unsigned i = placeholder.fragment_start; i < placeholder.fragment_end; + i++) { + if (RefPtr<NGPhysicalFragment>& child = children[i]) { + box.AddChild(std::move(child), offsets[i] - box_offset); + DCHECK(!children[i]); + } + } + + box.SetWritingMode(line_box->WritingMode()); + box.SetDirection(placeholder.item->Direction()); + box.SetSize(placeholder.size); + // TODO(kojii): Overflow size should be computed from children. + box.SetOverflowSize(placeholder.size); + RefPtr<NGLayoutResult> layout_result = box.ToBoxFragment(); + DCHECK(!children[placeholder.fragment_end]); + children[placeholder.fragment_end] = + std::move(layout_result->MutablePhysicalFragment()); + } + box_placeholders_.clear(); + + // Add the list to line_box by skipping null fragments; i.e., fragments added + // to box children. + for (unsigned i = 0; i < children.size(); i++) { + if (children[i]) + line_box->AddChild(children[i], offsets[i]); + } +} + NGInlineLayoutStateStack::PositionPending NGInlineLayoutStateStack::ApplyBaselineShift(NGInlineBoxState* box, NGLineBoxFragmentBuilder* line_box,
diff --git a/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_box_state.h b/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_box_state.h index 96106ab..5fcc396 100644 --- a/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_box_state.h +++ b/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_box_state.h
@@ -6,6 +6,7 @@ #define NGInlineBoxState_h #include "core/CoreExport.h" +#include "core/layout/ng/geometry/ng_logical_size.h" #include "core/layout/ng/inline/ng_line_height_metrics.h" #include "core/style/ComputedStyleConstants.h" #include "platform/LayoutUnit.h" @@ -16,7 +17,6 @@ class NGInlineItem; class NGLineBoxFragmentBuilder; -class NGTextFragmentBuilder; // Fragments that require the layout position/size of ancestor are packed in // this struct. @@ -32,12 +32,21 @@ // require ancestor position or size. struct NGInlineBoxState { unsigned fragment_start; + const NGInlineItem* item; const ComputedStyle* style; NGLineHeightMetrics metrics; NGLineHeightMetrics text_metrics; LayoutUnit text_top; + + // These values are to create a box fragment. Set only when needs_box_fragment + // is set. + LayoutUnit line_left_position; + LayoutUnit borders_paddings_block_start; + LayoutUnit borders_paddings_block_height; + Vector<NGPendingPositions> pending_descendants; bool include_used_fonts = false; + bool needs_box_fragment = false; // Compute text metrics for a box. All text in a box share the same metrics. void ComputeTextMetrics(const ComputedStyle& style, FontBaseline); @@ -45,6 +54,11 @@ unsigned start, unsigned end, FontBaseline); + + // Create a box fragment for this box. + void SetNeedsBoxFragment(LayoutUnit line_left_position, + LayoutUnit borders_paddings_block_start, + LayoutUnit borders_paddings_block_height); }; // Represents the inline tree structure. This class provides: @@ -61,23 +75,33 @@ NGInlineBoxState* OnBeginPlaceItems(const ComputedStyle*, FontBaseline); // Push a box state stack. - NGInlineBoxState* OnOpenTag(const NGInlineItem&, - NGLineBoxFragmentBuilder*, - NGTextFragmentBuilder*); + NGInlineBoxState* OnOpenTag(const NGInlineItem&, NGLineBoxFragmentBuilder*); // Pop a box state stack. NGInlineBoxState* OnCloseTag(const NGInlineItem&, NGLineBoxFragmentBuilder*, NGInlineBoxState*, - FontBaseline); + FontBaseline, + LayoutUnit position); // Compute all the pending positioning at the end of a line. - void OnEndPlaceItems(NGLineBoxFragmentBuilder*, FontBaseline); + void OnEndPlaceItems(NGLineBoxFragmentBuilder*, + FontBaseline, + LayoutUnit position); private: // End of a box state, either explicitly by close tag, or implicitly at the // end of a line. - void EndBoxState(NGInlineBoxState*, NGLineBoxFragmentBuilder*, FontBaseline); + void EndBoxState(NGInlineBoxState*, + NGLineBoxFragmentBuilder*, + FontBaseline, + LayoutUnit position); + + void AddBoxFragmentPlaceholder(NGInlineBoxState*, + NGLineBoxFragmentBuilder*, + FontBaseline, + LayoutUnit position); + void CreateBoxFragments(NGLineBoxFragmentBuilder*); enum PositionPending { kPositionNotPending, kPositionPending }; @@ -91,7 +115,16 @@ NGLineBoxFragmentBuilder*, FontBaseline); + // Data for a box fragment placeholder. See AddBoxFragmentPlaceholder(). + struct BoxFragmentPlaceholder { + unsigned fragment_start; + unsigned fragment_end; + const NGInlineItem* item; + NGLogicalSize size; + }; + Vector<NGInlineBoxState, 4> stack_; + Vector<BoxFragmentPlaceholder, 4> box_placeholders_; }; } // namespace blink
diff --git a/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_layout_algorithm.cc b/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_layout_algorithm.cc index 95c075b..f9b7422 100644 --- a/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_layout_algorithm.cc +++ b/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_layout_algorithm.cc
@@ -222,6 +222,7 @@ NGLineHeightMetrics line_metrics_with_leading = line_metrics; line_metrics_with_leading.AddLeading(line_style.ComputedLineHeightAsFixed()); NGLineBoxFragmentBuilder line_box(Node()); + line_box.SetWritingMode(ConstraintSpace().WritingMode()); // Compute heights of all inline items by placing the dominant baseline at 0. // The baseline is adjusted after the height of the line box is computed. @@ -253,13 +254,26 @@ item_result.end_offset); line_box.AddChild(std::move(text_fragment), {position, box->text_top}); } else if (item.Type() == NGInlineItem::kOpenTag) { - box = box_states_.OnOpenTag(item, &line_box, &text_builder); + box = box_states_.OnOpenTag(item, &line_box); // Compute text metrics for all inline boxes since even empty inlines // influence the line height. // https://drafts.csswg.org/css2/visudet.html#line-height box->ComputeTextMetrics(*item.Style(), baseline_type_); + text_builder.SetDirection(box->style->Direction()); + // TODO(kojii): We may need more conditions to create box fragments. + if (item.Style()->HasBoxDecorationBackground()) { + // TODO(kojii): These are once computed in NGLineBreaker. Should copy to + // NGInlineItemResult to reuse here. + NGBoxStrut borders = ComputeBorders(*constraint_space_, *item.Style()); + NGBoxStrut paddings = ComputePadding(*constraint_space_, *item.Style()); + // TODO(kojii): Set paint edges. + box->SetNeedsBoxFragment(position, + borders.block_start + paddings.block_start, + borders.BlockSum() + paddings.BlockSum()); + } } else if (item.Type() == NGInlineItem::kCloseTag) { - box = box_states_.OnCloseTag(item, &line_box, box, baseline_type_); + box = box_states_.OnCloseTag(item, &line_box, box, baseline_type_, + position); } else if (item.Type() == NGInlineItem::kAtomicInline) { box = PlaceAtomicInline(item, &item_result, position, &line_box, &text_builder); @@ -286,7 +300,7 @@ return true; // The line was empty. } - box_states_.OnEndPlaceItems(&line_box, baseline_type_); + box_states_.OnEndPlaceItems(&line_box, baseline_type_, position); // The baselines are always placed at pixel boundaries. Not doing so results // in incorrect layout of text decorations, most notably underlines. @@ -342,7 +356,7 @@ NGTextFragmentBuilder* text_builder) { DCHECK(item_result->layout_result); - NGInlineBoxState* box = box_states_.OnOpenTag(item, line_box, text_builder); + NGInlineBoxState* box = box_states_.OnOpenTag(item, line_box); // For replaced elements, inline-block elements, and inline-table elements, // the height is the height of their margin box. @@ -371,6 +385,7 @@ // TODO(kojii): Try to eliminate the wrapping text fragment and use the // |fragment| directly. Currently |CopyFragmentDataToLayoutBlockFlow| // requires a text fragment. + text_builder->SetDirection(item.Style()->Direction()); text_builder->SetSize({fragment.InlineSize(), block_size}); LayoutUnit line_top = item_result->margins.block_start - metrics.ascent; RefPtr<NGPhysicalTextFragment> text_fragment = text_builder->ToTextFragment( @@ -378,7 +393,8 @@ item_result->end_offset); line_box->AddChild(std::move(text_fragment), {position, line_top}); - return box_states_.OnCloseTag(item, line_box, box, baseline_type_); + return box_states_.OnCloseTag(item, line_box, box, baseline_type_, + LayoutUnit(0)); } void NGInlineLayoutAlgorithm::FindNextLayoutOpportunity() {
diff --git a/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_node.cc b/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_node.cc index 734d6ec..31440806 100644 --- a/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_node.cc +++ b/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_node.cc
@@ -33,28 +33,120 @@ namespace { -// Set the geometry to InlineFlowBox by computing the union of children. -void PlaceInlineFlowBoxes(InlineFlowBox* flow_box) { +struct FragmentPosition { + NGLogicalOffset offset; + LayoutUnit inline_size; +}; + +// Create BidiRuns from a list of NGPhysicalFragment. +// Produce a FragmentPosition map to place InlineBoxes. +void CreateBidiRuns(BidiRunList<BidiRun>* bidi_runs, + const Vector<RefPtr<NGPhysicalFragment>>& children, + const NGConstraintSpace& constraint_space, + NGLogicalOffset parent_offset, + const Vector<NGInlineItem>& items, + const Vector<unsigned, 32>& text_offsets, + Vector<FragmentPosition, 32>* positions_for_bidi_runs_out, + HashMap<LineLayoutItem, FragmentPosition>* positions_out) { + for (const auto& child : children) { + if (child->Type() == NGPhysicalFragment::kFragmentText) { + const auto* physical_fragment = ToNGPhysicalTextFragment(child.Get()); + const NGInlineItem& item = items[physical_fragment->ItemIndex()]; + BidiRun* run; + if (item.Type() == NGInlineItem::kText || + item.Type() == NGInlineItem::kControl) { + LayoutObject* layout_object = item.GetLayoutObject(); + DCHECK(layout_object->IsText()); + unsigned text_offset = text_offsets[physical_fragment->ItemIndex()]; + run = new BidiRun(physical_fragment->StartOffset() - text_offset, + physical_fragment->EndOffset() - text_offset, + item.BidiLevel(), LineLayoutItem(layout_object)); + layout_object->ClearNeedsLayout(); + } else if (item.Type() == NGInlineItem::kAtomicInline) { + LayoutObject* layout_object = item.GetLayoutObject(); + DCHECK(layout_object->IsAtomicInlineLevel()); + run = + new BidiRun(0, 1, item.BidiLevel(), LineLayoutItem(layout_object)); + } else { + continue; + } + bidi_runs->AddRun(run); + NGTextFragment fragment(constraint_space.WritingMode(), + physical_fragment); + // Store text fragments in a vector in the same order as BidiRunList. + // One LayoutText may produce multiple text fragments that they can't + // be set to a map. + positions_for_bidi_runs_out->push_back(FragmentPosition{ + fragment.Offset() + parent_offset, fragment.InlineSize()}); + } else { + DCHECK_EQ(child->Type(), NGPhysicalFragment::kFragmentBox); + NGPhysicalBoxFragment* physical_fragment = + ToNGPhysicalBoxFragment(child.Get()); + NGBoxFragment fragment(constraint_space.WritingMode(), physical_fragment); + NGLogicalOffset child_offset = fragment.Offset() + parent_offset; + CreateBidiRuns(bidi_runs, physical_fragment->Children(), constraint_space, + child_offset, items, text_offsets, + positions_for_bidi_runs_out, positions_out); + // Store box fragments in a map by LineLayoutItem. + positions_out->Set(LineLayoutItem(child->GetLayoutObject()), + FragmentPosition{child_offset, fragment.InlineSize()}); + } + } +} + +// Set the geometry to InlineBoxes by using the FragmentPosition map. +// When the map doesn't provide positions; i.e., when InlineFlowBox doesn't have +// corresponding box fragment, compute the union of children. +unsigned PlaceInlineBoxChildren( + InlineFlowBox* parent, + const Vector<FragmentPosition, 32>& positions_for_bidi_runs, + const HashMap<LineLayoutItem, FragmentPosition>& positions, + unsigned text_index = 0, + bool set_parent_position_from_children = false) { LayoutUnit logical_left = LayoutUnit::Max(); LayoutUnit logical_right = LayoutUnit::Min(); LayoutUnit logical_top = LayoutUnit::Max(); - for (InlineBox* curr = flow_box->FirstChild(); curr; - curr = curr->NextOnLine()) { - if (curr->GetLineLayoutItem().IsLayoutInline()) { - InlineFlowBox* flow = ToInlineFlowBox(curr); - PlaceInlineFlowBoxes(flow); + for (InlineBox* inline_box = parent->FirstChild(); inline_box; + inline_box = inline_box->NextOnLine()) { + if (inline_box->IsInlineFlowBox()) { + const auto& iter = positions.find(inline_box->GetLineLayoutItem()); + if (iter != positions.end()) { + const FragmentPosition& position = iter->value; + inline_box->SetLogicalLeft(position.offset.inline_offset); + inline_box->SetLogicalTop(position.offset.block_offset); + inline_box->SetLogicalWidth(position.inline_size); + } + + text_index = PlaceInlineBoxChildren(ToInlineFlowBox(inline_box), + positions_for_bidi_runs, positions, + text_index, iter == positions.end()); + } else { + const FragmentPosition& position = positions_for_bidi_runs[text_index++]; + inline_box->SetLogicalLeft(position.offset.inline_offset); + inline_box->SetLogicalTop(position.offset.block_offset); + inline_box->SetLogicalWidth(position.inline_size); + if (inline_box->GetLineLayoutItem().IsBox()) { + LineLayoutBox box(inline_box->GetLineLayoutItem()); + box.SetLocation(inline_box->Location()); + } } - logical_left = std::min(curr->LogicalLeft(), logical_left); - logical_right = std::max(curr->LogicalRight(), logical_right); - logical_top = std::min(curr->LogicalTop(), logical_top); + + if (set_parent_position_from_children) { + logical_left = std::min(inline_box->LogicalLeft(), logical_left); + logical_right = std::max(inline_box->LogicalRight(), logical_right); + logical_top = std::min(inline_box->LogicalTop(), logical_top); + } } - if (logical_left == LayoutUnit::Max()) - return; - logical_left -= flow_box->MarginBorderPaddingLogicalLeft(); - logical_right += flow_box->MarginBorderPaddingLogicalRight(); - flow_box->SetLogicalLeft(logical_left); - flow_box->SetLogicalWidth(logical_right - logical_left); - flow_box->SetLogicalTop(logical_top); + + if (set_parent_position_from_children && logical_left != LayoutUnit::Max()) { + logical_left -= parent->MarginBorderPaddingLogicalLeft(); + logical_right += parent->MarginBorderPaddingLogicalRight(); + parent->SetLogicalLeft(logical_left); + parent->SetLogicalWidth(logical_right - logical_left); + parent->SetLogicalTop(logical_top); + } + + return text_index; } } // namespace @@ -324,8 +416,8 @@ ? FontBaseline::kAlphabeticBaseline : FontBaseline::kIdeographicBaseline; - Vector<const NGPhysicalFragment*, 32> fragments_for_bidi_runs; - fragments_for_bidi_runs.ReserveInitialCapacity(items.size()); + Vector<FragmentPosition, 32> positions_for_bidi_runs; + HashMap<LineLayoutItem, FragmentPosition> positions; BidiRunList<BidiRun> bidi_runs; LineInfo line_info; NGPhysicalBoxFragment* box_fragment = @@ -333,31 +425,11 @@ for (const auto& container_child : box_fragment->Children()) { NGPhysicalLineBoxFragment* physical_line_box = ToNGPhysicalLineBoxFragment(container_child.Get()); + // Create a BidiRunList for this line. - for (const auto& line_child : physical_line_box->Children()) { - const auto* text_fragment = ToNGPhysicalTextFragment(line_child.Get()); - const NGInlineItem& item = items[text_fragment->ItemIndex()]; - BidiRun* run; - if (item.Type() == NGInlineItem::kText || - item.Type() == NGInlineItem::kControl) { - LayoutObject* layout_object = item.GetLayoutObject(); - DCHECK(layout_object->IsText()); - unsigned text_offset = text_offsets[text_fragment->ItemIndex()]; - run = new BidiRun(text_fragment->StartOffset() - text_offset, - text_fragment->EndOffset() - text_offset, - item.BidiLevel(), LineLayoutItem(layout_object)); - layout_object->ClearNeedsLayout(); - } else if (item.Type() == NGInlineItem::kAtomicInline) { - LayoutObject* layout_object = item.GetLayoutObject(); - DCHECK(layout_object->IsAtomicInlineLevel()); - run = - new BidiRun(0, 1, item.BidiLevel(), LineLayoutItem(layout_object)); - } else { - continue; - } - bidi_runs.AddRun(run); - fragments_for_bidi_runs.push_back(text_fragment); - } + CreateBidiRuns(&bidi_runs, physical_line_box->Children(), constraint_space, + NGLogicalOffset(), items, text_offsets, + &positions_for_bidi_runs, &positions); // TODO(kojii): bidi needs to find the logical last run. bidi_runs.SetLogicallyLastRun(bidi_runs.LastRun()); @@ -369,27 +441,7 @@ block_flow->ConstructLine(bidi_runs, line_info); // Copy fragments data to InlineBoxes. - DCHECK_EQ(fragments_for_bidi_runs.size(), bidi_runs.RunCount()); - BidiRun* run = bidi_runs.FirstRun(); - for (auto* physical_fragment : fragments_for_bidi_runs) { - DCHECK(run); - NGTextFragment fragment(constraint_space.WritingMode(), - ToNGPhysicalTextFragment(physical_fragment)); - InlineBox* inline_box = run->box_; - inline_box->SetLogicalWidth(fragment.InlineSize()); - inline_box->SetLogicalLeft(fragment.InlineOffset()); - inline_box->SetLogicalTop(fragment.BlockOffset()); - if (inline_box->GetLineLayoutItem().IsBox()) { - LineLayoutBox box(inline_box->GetLineLayoutItem()); - box.SetLocation(inline_box->Location()); - } - run = run->Next(); - } - DCHECK(!run); - - // InlineTextBox and InlineBox are placed, but when ConstructLine() created - // InlineFlowBox, they needed to be placed as well. - PlaceInlineFlowBoxes(root_line_box); + PlaceInlineBoxChildren(root_line_box, positions_for_bidi_runs, positions); // Copy to RootInlineBox. NGLineBoxFragment line_box(constraint_space.WritingMode(), @@ -405,7 +457,8 @@ line_top, baseline + max_with_leading.descent); bidi_runs.DeleteRuns(); - fragments_for_bidi_runs.clear(); + positions_for_bidi_runs.clear(); + positions.clear(); } }
diff --git a/third_party/WebKit/Source/core/layout/ng/inline/ng_line_box_fragment_builder.cc b/third_party/WebKit/Source/core/layout/ng/inline/ng_line_box_fragment_builder.cc index 8c1b2b5..b7da848 100644 --- a/third_party/WebKit/Source/core/layout/ng/inline/ng_line_box_fragment_builder.cc +++ b/third_party/WebKit/Source/core/layout/ng/inline/ng_line_box_fragment_builder.cc
@@ -14,7 +14,15 @@ namespace blink { NGLineBoxFragmentBuilder::NGLineBoxFragmentBuilder(NGInlineNode* node) - : direction_(TextDirection::kLtr), node_(node) {} + : writing_mode_(kHorizontalTopBottom), + direction_(TextDirection::kLtr), + node_(node) {} + +NGLineBoxFragmentBuilder& NGLineBoxFragmentBuilder::SetWritingMode( + NGWritingMode writing_mode) { + writing_mode_ = writing_mode; + return *this; +} NGLineBoxFragmentBuilder& NGLineBoxFragmentBuilder::SetDirection( TextDirection direction) {
diff --git a/third_party/WebKit/Source/core/layout/ng/inline/ng_line_box_fragment_builder.h b/third_party/WebKit/Source/core/layout/ng/inline/ng_line_box_fragment_builder.h index f4e4da8..aa09de1 100644 --- a/third_party/WebKit/Source/core/layout/ng/inline/ng_line_box_fragment_builder.h +++ b/third_party/WebKit/Source/core/layout/ng/inline/ng_line_box_fragment_builder.h
@@ -22,6 +22,9 @@ public: explicit NGLineBoxFragmentBuilder(NGInlineNode*); + NGLineBoxFragmentBuilder& SetWritingMode(NGWritingMode); + NGWritingMode WritingMode() const { return writing_mode_; } + NGLineBoxFragmentBuilder& SetDirection(TextDirection); NGLineBoxFragmentBuilder& SetInlineSize(LayoutUnit); @@ -36,6 +39,9 @@ const Vector<RefPtr<NGPhysicalFragment>>& Children() const { return children_; } + Vector<RefPtr<NGPhysicalFragment>>& MutableChildren() { return children_; } + const Vector<NGLogicalOffset>& Offsets() const { return offsets_; } + Vector<NGLogicalOffset>& MutableOffsets() { return offsets_; } void SetMetrics(const NGLineHeightMetrics&); const NGLineHeightMetrics& Metrics() const { return metrics_; } @@ -48,6 +54,7 @@ RefPtr<NGPhysicalLineBoxFragment> ToLineBoxFragment(); private: + NGWritingMode writing_mode_; TextDirection direction_; Persistent<NGInlineNode> node_;
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_fragment.cc b/third_party/WebKit/Source/core/layout/ng/ng_fragment.cc index e9b12536..b1f8d45 100644 --- a/third_party/WebKit/Source/core/layout/ng/ng_fragment.cc +++ b/third_party/WebKit/Source/core/layout/ng/ng_fragment.cc
@@ -37,6 +37,10 @@ : physical_fragment_->Offset().left; } +NGLogicalOffset NGFragment::Offset() const { + return NGLogicalOffset(InlineOffset(), BlockOffset()); +} + NGPhysicalFragment::NGFragmentType NGFragment::Type() const { return physical_fragment_->Type(); }
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_fragment.h b/third_party/WebKit/Source/core/layout/ng/ng_fragment.h index 87d0ed3e..4c43104f 100644 --- a/third_party/WebKit/Source/core/layout/ng/ng_fragment.h +++ b/third_party/WebKit/Source/core/layout/ng/ng_fragment.h
@@ -6,6 +6,7 @@ #define NGFragment_h #include "core/CoreExport.h" +#include "core/layout/ng/geometry/ng_logical_offset.h" #include "core/layout/ng/ng_physical_fragment.h" #include "core/layout/ng/ng_writing_mode.h" #include "platform/LayoutUnit.h" @@ -31,6 +32,7 @@ // Returns the offset relative to the parent fragment's content-box. LayoutUnit InlineOffset() const; LayoutUnit BlockOffset() const; + NGLogicalOffset Offset() const; NGPhysicalFragment::NGFragmentType Type() const;
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_fragment_builder.cc b/third_party/WebKit/Source/core/layout/ng/ng_fragment_builder.cc index b8739da..baed4d6 100644 --- a/third_party/WebKit/Source/core/layout/ng/ng_fragment_builder.cc +++ b/third_party/WebKit/Source/core/layout/ng/ng_fragment_builder.cc
@@ -22,8 +22,16 @@ writing_mode_(kHorizontalTopBottom), direction_(TextDirection::kLtr), node_(node), - did_break_(false) { -} + layout_object_(node->GetLayoutObject()), + did_break_(false) {} + +NGFragmentBuilder::NGFragmentBuilder(NGPhysicalFragment::NGFragmentType type, + LayoutObject* layout_object) + : type_(type), + writing_mode_(kHorizontalTopBottom), + direction_(TextDirection::kLtr), + layout_object_(layout_object), + did_break_(false) {} NGFragmentBuilder& NGFragmentBuilder::SetWritingMode( NGWritingMode writing_mode) { @@ -85,8 +93,10 @@ switch (child->Type()) { case NGPhysicalBoxFragment::kFragmentBox: // Update if we have fragmented in this flow. - did_break_ |= !child->BreakToken()->IsFinished(); - child_break_tokens_.push_back(child->BreakToken()); + if (child->BreakToken()) { + did_break_ |= !child->BreakToken()->IsFinished(); + child_break_tokens_.push_back(child->BreakToken()); + } break; case NGPhysicalBoxFragment::kFragmentLineBox: // NGInlineNode produces multiple line boxes in an anonymous box. Only @@ -193,16 +203,18 @@ } RefPtr<NGBreakToken> break_token; - if (last_inline_break_token_) { - DCHECK(!last_inline_break_token_->IsFinished()); - child_break_tokens_.push_back(std::move(last_inline_break_token_)); - did_break_ = true; - } - if (did_break_) { - break_token = NGBlockBreakToken::Create(node_.Get(), used_block_size_, - child_break_tokens_); - } else { - break_token = NGBlockBreakToken::Create(node_.Get()); + if (node_) { + if (last_inline_break_token_) { + DCHECK(!last_inline_break_token_->IsFinished()); + child_break_tokens_.push_back(std::move(last_inline_break_token_)); + did_break_ = true; + } + if (did_break_) { + break_token = NGBlockBreakToken::Create(node_.Get(), used_block_size_, + child_break_tokens_); + } else { + break_token = NGBlockBreakToken::Create(node_.Get()); + } } for (auto& positioned_float : positioned_floats_) { @@ -214,9 +226,9 @@ } RefPtr<NGPhysicalBoxFragment> fragment = AdoptRef(new NGPhysicalBoxFragment( - node_->GetLayoutObject(), physical_size, - overflow_.ConvertToPhysical(writing_mode_), children_, positioned_floats_, - bfc_offset_, end_margin_strut_, std::move(break_token))); + layout_object_, physical_size, overflow_.ConvertToPhysical(writing_mode_), + children_, positioned_floats_, bfc_offset_, end_margin_strut_, + std::move(break_token))); return AdoptRef( new NGLayoutResult(std::move(fragment), out_of_flow_descendants_,
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_fragment_builder.h b/third_party/WebKit/Source/core/layout/ng/ng_fragment_builder.h index d10584c..24f44a3 100644 --- a/third_party/WebKit/Source/core/layout/ng/ng_fragment_builder.h +++ b/third_party/WebKit/Source/core/layout/ng/ng_fragment_builder.h
@@ -24,6 +24,10 @@ public: NGFragmentBuilder(NGPhysicalFragment::NGFragmentType, NGLayoutInputNode*); + // Build a fragment for LayoutObject without NGLayoutInputNode. LayoutInline + // has NGInlineItem but does not have corresponding NGLayoutInputNode. + NGFragmentBuilder(NGPhysicalFragment::NGFragmentType, LayoutObject*); + using WeakBoxList = PersistentHeapLinkedHashSet<WeakMember<NGBlockNode>>; NGFragmentBuilder& SetWritingMode(NGWritingMode); @@ -149,6 +153,7 @@ TextDirection direction_; Persistent<NGLayoutInputNode> node_; + LayoutObject* layout_object_; NGLogicalSize size_; NGLogicalSize overflow_;
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_layout_result.h b/third_party/WebKit/Source/core/layout/ng/ng_layout_result.h index 0ac755a..77d7be6a 100644 --- a/third_party/WebKit/Source/core/layout/ng/ng_layout_result.h +++ b/third_party/WebKit/Source/core/layout/ng/ng_layout_result.h
@@ -28,6 +28,9 @@ RefPtr<NGPhysicalFragment> PhysicalFragment() const { return physical_fragment_; } + RefPtr<NGPhysicalFragment>& MutablePhysicalFragment() { + return physical_fragment_; + } const HeapLinkedHashSet<WeakMember<NGBlockNode>>& OutOfFlowDescendants() const {