Chromium Code Reviews| Index: third_party/WebKit/Source/core/editing/LayoutSelection.cpp |
| diff --git a/third_party/WebKit/Source/core/editing/LayoutSelection.cpp b/third_party/WebKit/Source/core/editing/LayoutSelection.cpp |
| index 3beb0274d57c55771ddea0e7a79747ee84b50166..0e9176446cf1a7e8de9284a5cd085dafd5b27170 100644 |
| --- a/third_party/WebKit/Source/core/editing/LayoutSelection.cpp |
| +++ b/third_party/WebKit/Source/core/editing/LayoutSelection.cpp |
| @@ -28,6 +28,10 @@ |
| #include "core/editing/VisibleUnits.h" |
| #include "core/html/TextControlElement.h" |
| #include "core/layout/LayoutView.h" |
| +#include "core/layout/ng/inline/ng_inline_items_builder.h" |
| +#include "core/layout/ng/inline/ng_inline_node.h" |
| +#include "core/layout/ng/inline/ng_inline_node_data.h" |
| +#include "core/layout/ng/layout_ng_block_flow.h" |
| #include "core/paint/PaintLayer.h" |
| namespace blink { |
| @@ -55,29 +59,25 @@ bool SelectionPaintRange::operator==(const SelectionPaintRange& other) const { |
| } |
| LayoutObject* SelectionPaintRange::StartLayoutObject() const { |
| - DCHECK(!IsNull()); |
| return start_layout_object_; |
| } |
| int SelectionPaintRange::StartOffset() const { |
| - DCHECK(!IsNull()); |
| return start_offset_; |
| } |
| LayoutObject* SelectionPaintRange::EndLayoutObject() const { |
| - DCHECK(!IsNull()); |
| return end_layout_object_; |
| } |
| int SelectionPaintRange::EndOffset() const { |
| - DCHECK(!IsNull()); |
| return end_offset_; |
| } |
| LayoutSelection::LayoutSelection(FrameSelection& frame_selection) |
| : frame_selection_(&frame_selection), |
| has_pending_selection_(false), |
| - paint_range_(SelectionPaintRange()) {} |
| + paint_range_(nullptr) {} |
| static SelectionInFlatTree CalcSelection( |
| const VisibleSelectionInFlatTree& original_selection, |
| @@ -170,9 +170,6 @@ enum class CollectSelectedMapOption { |
| static SelectedMap CollectSelectedMap(const SelectionPaintRange& range, |
| CollectSelectedMapOption option) { |
| - if (range.IsNull()) |
| - return SelectedMap(); |
| - |
| SelectedMap selected_map; |
| LayoutObject* const stop = |
| @@ -204,9 +201,6 @@ static SelectedMap CollectSelectedMap(const SelectionPaintRange& range, |
| // Update the selection status of all LayoutObjects between |start| and |end|. |
| static void SetSelectionState(const SelectionPaintRange& range) { |
| - if (range.IsNull()) |
| - return; |
| - |
| if (range.StartLayoutObject() == range.EndLayoutObject()) { |
| range.StartLayoutObject()->SetSelectionStateIfNeeded( |
| SelectionState::kStartAndEnd); |
| @@ -229,9 +223,11 @@ static void SetSelectionState(const SelectionPaintRange& range) { |
| // Set SetSelectionState and ShouldInvalidateSelection flag of LayoutObjects |
| // comparing them in |new_range| and |old_range|. |
| static void UpdateLayoutObjectState(const SelectionPaintRange& new_range, |
| - const SelectionPaintRange& old_range) { |
| + const SelectionPaintRange* old_range) { |
| SelectedMap old_selected_map = |
| - CollectSelectedMap(old_range, CollectSelectedMapOption::kCollectBlock); |
| + old_range ? CollectSelectedMap(*old_range, |
| + CollectSelectedMapOption::kCollectBlock) |
| + : SelectedMap(); |
| // Now clear the selection. |
| for (auto layout_object : old_selected_map.object_map.Keys()) |
| @@ -254,9 +250,9 @@ static void UpdateLayoutObjectState(const SelectionPaintRange& new_range, |
| SelectionState old_selection_state = pair.value; |
| if (new_selection_state != old_selection_state || |
| (new_range.StartLayoutObject() == obj && |
| - new_range.StartOffset() != old_range.StartOffset()) || |
| + new_range.StartOffset() != old_range->StartOffset()) || |
| (new_range.EndLayoutObject() == obj && |
| - new_range.EndOffset() != old_range.EndOffset())) { |
| + new_range.EndOffset() != old_range->EndOffset())) { |
| obj->SetShouldInvalidateSelection(); |
| new_selected_map.object_map.erase(obj); |
| } |
| @@ -284,11 +280,46 @@ static void UpdateLayoutObjectState(const SelectionPaintRange& new_range, |
| layout_object->SetShouldInvalidateSelection(); |
| } |
| +static LayoutBlockFlow* MostRecentLayoutBlockFlow(LayoutObject* layout_object) { |
| + for (LayoutObject* runner = layout_object; runner && !runner->IsLayoutView(); |
| + runner = runner->ContainingBlock()) { |
|
kojii
2017/06/16 11:25:56
why ContainingBlock(), not Parent()?
yoichio
2017/06/21 08:16:43
Perhaps its my misunderstanding. If LayoutBlockFlo
|
| + if (runner->IsLayoutBlockFlow()) |
| + return ToLayoutBlockFlow(runner); |
| + } |
| + return nullptr; |
| +} |
| + |
| +static int GetOffsetInMixedTree(Node* node, |
| + LayoutObject* layout_object, |
| + int offset) { |
| + LayoutBlockFlow* start_block_flow = MostRecentLayoutBlockFlow(layout_object); |
| + if (!start_block_flow || !start_block_flow->IsLayoutNGBlockFlow()) |
| + return offset; // Legay. Return DOM offset; |
|
yosin_UTC9
2017/06/16 02:04:20
nit: s/Legay/Legacy/
|
| + |
| + // TODO(yoichio): How about caching? Create takes |
| + // O(<LayoutNGBlockflow.Data.text_content_>). |
| + const NGTextOffsetMap& offset_map = |
| + NGTextOffsetMap::Create(*ToLayoutNGBlockFlowOrDie(start_block_flow)); |
| + const Optional<int> ng_offset = offset_map.Get(node, offset); |
| + return ng_offset.value(); |
| +} |
| + |
| std::pair<int, int> LayoutSelection::SelectionStartEnd() { |
| Commit(); |
| - if (paint_range_.IsNull()) |
| + if (!paint_range_) |
| return std::make_pair(-1, -1); |
| - return std::make_pair(paint_range_.StartOffset(), paint_range_.EndOffset()); |
| + if (!RuntimeEnabledFeatures::LayoutNGEnabled()) { |
| + return std::make_pair(paint_range_->StartOffset(), |
| + paint_range_->EndOffset()); |
| + } |
| + // Layout NG mapping |
| + const int start_offset_mixed = GetOffsetInMixedTree( |
| + paint_range_->StartNode(), paint_range_->StartLayoutObject(), |
| + paint_range_->StartOffset()); |
| + const int end_offset_mixed = GetOffsetInMixedTree( |
| + paint_range_->EndNode(), paint_range_->EndLayoutObject(), |
| + paint_range_->EndOffset()); |
| + return std::make_pair(start_offset_mixed, end_offset_mixed); |
| } |
| void LayoutSelection::ClearSelection() { |
| @@ -298,11 +329,11 @@ void LayoutSelection::ClearSelection() { |
| DisableCompositingQueryAsserts disabler; |
| // Just return if the selection is already empty. |
| - if (paint_range_.IsNull()) |
| + if (!paint_range_) |
| return; |
| const SelectedMap& old_selected_map = CollectSelectedMap( |
| - paint_range_, CollectSelectedMapOption::kNotCollectBlock); |
| + *paint_range_, CollectSelectedMapOption::kNotCollectBlock); |
| // Clear SelectionState and invalidation. |
| for (auto layout_object : old_selected_map.object_map.Keys()) { |
| const SelectionState old_state = layout_object->GetSelectionState(); |
| @@ -313,11 +344,76 @@ void LayoutSelection::ClearSelection() { |
| } |
| // Reset selection. |
| - paint_range_ = SelectionPaintRange(); |
| + paint_range_ = nullptr; |
| +} |
| + |
| +static SelectionPaintRange* CalcSelectionNG( |
|
yosin_UTC9
2017/06/15 09:51:27
If this function traverse flat tree and works on l
yoichio
2017/06/21 08:16:42
I will rewrite w/o most.*CaretPosition() functions
|
| + const FrameSelection& frame_selection) { |
| + const SelectionInDOMTree& selection_in_dom = |
| + frame_selection.GetSelectionInDOMTree(); |
| + if (selection_in_dom.IsNone()) |
| + return nullptr; |
| + |
| + // yoichio: Tthis should be on FlatTree. |
| + const Position& start = selection_in_dom.ComputeStartPosition(); |
|
yosin_UTC9
2017/06/15 09:39:02
Why don't you use flat tree version?
|
| + const Position& end = selection_in_dom.ComputeEndPosition(); |
| + LayoutObject* const start_layout_object = |
|
yosin_UTC9
2017/06/16 02:04:20
|start_layout_object| can be nullptr.
So, we need
|
| + start.AnchorNode()->GetLayoutObject(); |
| + LayoutObject* const end_layout_object = end.AnchorNode()->GetLayoutObject(); |
|
yosin_UTC9
2017/06/16 02:04:20
|end_layout_object_| can be nullptr.
So, we need t
|
| + |
| + Node* start_node = nullptr; |
| + LayoutObject* paint_range_start = nullptr; |
| + int paint_range_start_offset = -1; |
| + // Seek the first text node. |
| + for (LayoutObject* runner = start_layout_object; |
| + runner && runner != end_layout_object->NextInPreOrder(); |
| + runner = runner->NextInPreOrder()) { |
| + if (runner->IsText() && runner->GetNode()->IsTextNode()) { |
| + start_node = runner->GetNode(); |
| + paint_range_start = runner; |
| + if (runner == start_layout_object) { |
| + paint_range_start_offset = start.ComputeEditingOffset(); |
| + break; |
| + } |
| + paint_range_start_offset = 0; |
| + break; |
| + } |
| + } |
| + DCHECK(paint_range_start); |
| + // Should consider block cursor painting. |
| + Node* end_node = nullptr; |
| + LayoutObject* paint_range_end = nullptr; |
| + int paint_range_end_offset = -1; |
| + for (LayoutObject* runner = end_layout_object; |
| + runner && runner != start_layout_object->PreviousInPreOrder(); |
| + runner = runner->PreviousInPreOrder()) { |
| + if (runner->IsText() && runner->GetNode()->IsTextNode()) { |
| + end_node = runner->GetNode(); |
| + paint_range_end = runner; |
| + if (runner == end_layout_object) { |
| + paint_range_end_offset = end.ComputeEditingOffset(); |
| + break; |
| + } |
| + LayoutText* text = ToLayoutText(runner); |
| + paint_range_end_offset = text->TextLength(); |
| + break; |
| + } |
| + } |
| + DCHECK(paint_range_end); |
| + |
| + return new SelectionPaintRange(start_node, paint_range_start, |
| + paint_range_start_offset, end_node, |
| + paint_range_end, paint_range_end_offset); |
| } |
| +// if(RuntimeEnabledFeatures::LayoutNGEnabled()) { |
| -static SelectionPaintRange CalcSelectionPaintRange( |
| +static SelectionPaintRange* CalcSelectionPaintRange( |
| const FrameSelection& frame_selection) { |
| + if (RuntimeEnabledFeatures::LayoutNGEnabled()) |
| + return CalcSelectionNG(frame_selection); |
|
yosin_UTC9
2017/06/15 09:39:02
So, for painting selection, we need to have NG ver
|
| + |
| + // NGMemo: ComputeVisibleSelectionInFlatTree depends on offsetmapping from |
| + // DOM->NG. |
| const VisibleSelectionInFlatTree& original_selection = |
| frame_selection.ComputeVisibleSelectionInFlatTree(); |
| // Construct a new VisibleSolution, since visibleSelection() is not |
| @@ -330,7 +426,7 @@ static SelectionPaintRange CalcSelectionPaintRange( |
| CreateVisibleSelection(new_selection); |
| if (!selection.IsRange() || frame_selection.IsHidden()) |
| - return SelectionPaintRange(); |
| + return nullptr; |
| DCHECK(!selection.IsNone()); |
| // Use the rightmost candidate for the start of the selection, and the |
| @@ -358,9 +454,9 @@ static SelectionPaintRange CalcSelectionPaintRange( |
| DCHECK(end_layout_object); |
| DCHECK(start_layout_object->View() == end_layout_object->View()); |
| - return SelectionPaintRange(start_layout_object, |
| - start_pos.ComputeEditingOffset(), |
| - end_layout_object, end_pos.ComputeEditingOffset()); |
| + return new SelectionPaintRange( |
| + start_layout_object, start_pos.ComputeEditingOffset(), end_layout_object, |
| + end_pos.ComputeEditingOffset()); |
| } |
| void LayoutSelection::Commit() { |
| @@ -368,25 +464,24 @@ void LayoutSelection::Commit() { |
| return; |
| has_pending_selection_ = false; |
| - const SelectionPaintRange& new_range = |
| - CalcSelectionPaintRange(*frame_selection_); |
| - if (new_range.IsNull()) { |
| + SelectionPaintRange* new_range = CalcSelectionPaintRange(*frame_selection_); |
| + if (!new_range) { |
| ClearSelection(); |
| return; |
| } |
| // Just return if the selection hasn't changed. |
| - if (paint_range_ == new_range) |
| + if (paint_range_ && *paint_range_ == *new_range) |
| return; |
| DCHECK(frame_selection_->GetDocument().GetLayoutView()->GetFrameView()); |
| DCHECK(!frame_selection_->GetDocument().NeedsLayoutTreeUpdate()); |
| - UpdateLayoutObjectState(new_range, paint_range_); |
| + UpdateLayoutObjectState(*new_range, paint_range_); |
| paint_range_ = new_range; |
| } |
| void LayoutSelection::OnDocumentShutdown() { |
| has_pending_selection_ = false; |
| - paint_range_ = SelectionPaintRange(); |
| + paint_range_ = nullptr; |
| } |
| static LayoutRect SelectionRectForLayoutObject(const LayoutObject* object) { |
| @@ -407,15 +502,15 @@ IntRect LayoutSelection::SelectionBounds() { |
| VisitedContainingBlockSet visited_containing_blocks; |
| Commit(); |
| - if (paint_range_.IsNull()) |
| + if (!paint_range_) |
| return IntRect(); |
| - LayoutObject* os = paint_range_.StartLayoutObject(); |
| - LayoutObject* stop = LayoutObjectAfterPosition(paint_range_.EndLayoutObject(), |
| - paint_range_.EndOffset()); |
| + LayoutObject* os = paint_range_->StartLayoutObject(); |
| + LayoutObject* stop = LayoutObjectAfterPosition( |
| + paint_range_->EndLayoutObject(), paint_range_->EndOffset()); |
| while (os && os != stop) { |
| - if ((os->CanBeSelectionLeaf() || os == paint_range_.StartLayoutObject() || |
| - os == paint_range_.EndLayoutObject()) && |
| + if ((os->CanBeSelectionLeaf() || os == paint_range_->StartLayoutObject() || |
| + os == paint_range_->EndLayoutObject()) && |
| os->GetSelectionState() != SelectionState::kNone) { |
| // Blocks are responsible for painting line gaps and margin gaps. They |
| // must be examined as well. |
| @@ -438,15 +533,15 @@ IntRect LayoutSelection::SelectionBounds() { |
| } |
| void LayoutSelection::InvalidatePaintForSelection() { |
| - if (paint_range_.IsNull()) |
| + if (!paint_range_) |
| return; |
| - LayoutObject* end = LayoutObjectAfterPosition(paint_range_.EndLayoutObject(), |
| - paint_range_.EndOffset()); |
| - for (LayoutObject* o = paint_range_.StartLayoutObject(); o && o != end; |
| + LayoutObject* end = LayoutObjectAfterPosition(paint_range_->EndLayoutObject(), |
| + paint_range_->EndOffset()); |
| + for (LayoutObject* o = paint_range_->StartLayoutObject(); o && o != end; |
| o = o->NextInPreOrder()) { |
| - if (!o->CanBeSelectionLeaf() && o != paint_range_.StartLayoutObject() && |
| - o != paint_range_.EndLayoutObject()) |
| + if (!o->CanBeSelectionLeaf() && o != paint_range_->StartLayoutObject() && |
| + o != paint_range_->EndLayoutObject()) |
| continue; |
| if (o->GetSelectionState() == SelectionState::kNone) |
| continue; |
| @@ -457,6 +552,60 @@ void LayoutSelection::InvalidatePaintForSelection() { |
| DEFINE_TRACE(LayoutSelection) { |
| visitor->Trace(frame_selection_); |
| + visitor->Trace(paint_range_); |
| +} |
| + |
| +NGTextOffsetMap::Builder::Builder() : offset_map_(new NGTextOffsetMap) {} |
|
kojii
2017/06/16 11:25:56
so this part will be removed and not for review, c
|
| + |
| +void NGTextOffsetMap::Builder::Add(Node* node, |
| + int dom_offset, |
| + int layout_offset) { |
| + DCHECK(node); |
| + offset_map_->node_to_offset_map_.Set(std::make_pair(node, dom_offset), |
| + layout_offset); |
| +} |
| + |
| +NGTextOffsetMap NGTextOffsetMap::Builder::Build() { |
| + return std::move(*offset_map_); |
| +} |
| + |
| +Optional<int> NGTextOffsetMap::Get(Node* node, int dom_offset) const { |
| + if (!node) |
| + return Optional<int>(); |
| + auto iterator = node_to_offset_map_.find(std::make_pair(node, dom_offset)); |
| + if (iterator == node_to_offset_map_.end()) |
| + return Optional<int>(); |
| + return iterator->value; |
| +} |
| + |
| +NGTextOffsetMap NGTextOffsetMap::Create( |
| + const LayoutNGBlockFlow& ng_block_flow) { |
| + NGTextOffsetMap::Builder builder; |
| + Vector<NGInlineItem> pseudo_items; |
| + Vector<int> offset_mapping; |
| + offset_mapping.push_back(0); |
| + NGInlineItemsBuilder items_builder(&pseudo_items, &offset_mapping); |
| + |
| + LayoutObject* last_object = nullptr; |
| + for (const auto& item : ng_block_flow.GetNGInlineNodeData().items_) { |
| + LayoutObject* layout_object = item.GetLayoutObject(); |
| + if (!layout_object || !layout_object->IsText() || |
| + layout_object == last_object) |
| + continue; |
| + |
| + Text* text = ToText(layout_object->GetNode()); |
| + size_t begin = offset_mapping.size(); |
| + items_builder.Append(text->textContent(), layout_object->Style(), |
| + layout_object); |
| + size_t end = offset_mapping.size(); |
| + for (size_t i = 0; i < end - begin + 1; i++) { |
| + int ng_offset = offset_mapping[begin + i - 1]; |
| + builder.Add(text, i, ng_offset); |
| + } |
| + |
| + last_object = layout_object; |
| + } |
| + return builder.Build(); |
| } |
| } // namespace blink |