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

[FragmentItem] Support |LayoutObject::PositionForPoint|

This patch implements |NGFragmentItem| support for
|LayoutObject::PositionForPoint|.

When only |Node| is needed (e.g., hovering), |NodeAtPoint|
is used, but when text offset is needed (e.g., click),
|PositionForPoint| does the work. Clicking on text works now,
but draggig doesn't work yet. I'll work in following patches.

This includes a semantics change to |NGInlineCursor::
PositionForPoint|. Before this change, it returned the
position for the current item/fragment. The problem is
that the root |LayoutBlockFlow| cannot be the current item.
This patch changes it to return any items where the cursor
can traverse.

Fixes ~30 failures.

Bug: 982194
Change-Id: Ie4f26fcda0ecfd0084afe9ff5272d9f4e682452b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1948575
Commit-Queue: Koji Ishii <kojii@chromium.org>
Reviewed-by: default avatarIan Kilpatrick <ikilpatrick@chromium.org>
Cr-Commit-Position: refs/heads/master@{#721922}
parent e4bdb634
...@@ -72,7 +72,7 @@ PositionWithAffinityTemplate<Strategy> StartPositionForLine( ...@@ -72,7 +72,7 @@ PositionWithAffinityTemplate<Strategy> StartPositionForLine(
DCHECK(line_box.IsLineBox()) << line_box; DCHECK(line_box.IsLineBox()) << line_box;
const PhysicalOffset start_point = line_box.LineStartPoint(); const PhysicalOffset start_point = line_box.LineStartPoint();
return FromPositionInDOMTree<Strategy>( return FromPositionInDOMTree<Strategy>(
line_box.PositionForPoint(start_point)); line_box.CursorForDescendants().PositionForPoint(start_point));
} }
const InlineBox* inline_box = const InlineBox* inline_box =
...@@ -254,7 +254,7 @@ static PositionWithAffinityTemplate<Strategy> EndPositionForLine( ...@@ -254,7 +254,7 @@ static PositionWithAffinityTemplate<Strategy> EndPositionForLine(
line_box.MoveToContainingLine(); line_box.MoveToContainingLine();
const PhysicalOffset end_point = line_box.LineEndPoint(); const PhysicalOffset end_point = line_box.LineEndPoint();
return FromPositionInDOMTree<Strategy>( return FromPositionInDOMTree<Strategy>(
line_box.PositionForPoint(end_point)); line_box.CursorForDescendants().PositionForPoint(end_point));
} }
const InlineBox* inline_box = const InlineBox* inline_box =
......
...@@ -4,7 +4,9 @@ ...@@ -4,7 +4,9 @@
#include "third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.h"
#include "third_party/blink/renderer/core/editing/inline_box_traversal.h"
#include "third_party/blink/renderer/core/editing/position_with_affinity.h" #include "third_party/blink/renderer/core/editing/position_with_affinity.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_caret_position.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items_builder.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items_builder.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.h"
#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h" #include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h"
...@@ -352,6 +354,55 @@ void NGFragmentItem::SetDeltaToNextForSameLayoutObject(wtf_size_t delta) { ...@@ -352,6 +354,55 @@ void NGFragmentItem::SetDeltaToNextForSameLayoutObject(wtf_size_t delta) {
delta_to_next_for_same_layout_object_ = delta; delta_to_next_for_same_layout_object_ = delta;
} }
PositionWithAffinity NGFragmentItem::PositionForPointInText(
const PhysicalOffset& point,
const NGInlineCursor& cursor) const {
DCHECK_EQ(Type(), kText);
DCHECK_EQ(cursor.CurrentItem(), this);
const unsigned text_offset = TextOffsetForPoint(point, cursor.Items());
const NGCaretPosition unadjusted_position{
cursor, NGCaretPositionType::kAtTextOffset, text_offset};
if (RuntimeEnabledFeatures::BidiCaretAffinityEnabled())
return unadjusted_position.ToPositionInDOMTreeWithAffinity();
if (text_offset > StartOffset() && text_offset < EndOffset())
return unadjusted_position.ToPositionInDOMTreeWithAffinity();
return BidiAdjustment::AdjustForHitTest(unadjusted_position)
.ToPositionInDOMTreeWithAffinity();
}
unsigned NGFragmentItem::TextOffsetForPoint(
const PhysicalOffset& point,
const NGFragmentItems& items) const {
DCHECK_EQ(Type(), kText);
const ComputedStyle& style = Style();
const LayoutUnit& point_in_line_direction =
style.IsHorizontalWritingMode() ? point.left : point.top;
if (const ShapeResultView* shape_result = TextShapeResult()) {
// TODO(layout-dev): Move caret logic out of ShapeResult into separate
// support class for code health and to avoid this copy.
return shape_result->CreateShapeResult()->CaretOffsetForHitTest(
point_in_line_direction.ToFloat(), Text(items), BreakGlyphs) +
StartOffset();
}
// Flow control fragments such as forced line break, tabulation, soft-wrap
// opportunities, etc. do not have ShapeResult.
DCHECK(IsFlowControl());
// Zero-inline-size objects such as newline always return the start offset.
LogicalSize size = Size().ConvertToLogical(style.GetWritingMode());
if (!size.inline_size)
return StartOffset();
// Sized objects such as tabulation returns the next offset if the given point
// is on the right half.
LayoutUnit inline_offset = IsLtr(ResolvedDirection())
? point_in_line_direction
: size.inline_size - point_in_line_direction;
DCHECK_EQ(1u, TextLength());
return inline_offset <= size.inline_size / 2 ? StartOffset() : EndOffset();
}
NGFragmentItem::ItemsForLayoutObject NGFragmentItem::ItemsFor( NGFragmentItem::ItemsForLayoutObject NGFragmentItem::ItemsFor(
const LayoutObject& layout_object) { const LayoutObject& layout_object) {
DCHECK(layout_object.IsInLayoutNGInlineFormattingContext()); DCHECK(layout_object.IsInLayoutNGInlineFormattingContext());
......
...@@ -339,6 +339,11 @@ class CORE_EXPORT NGFragmentItem : public DisplayItemClient { ...@@ -339,6 +339,11 @@ class CORE_EXPORT NGFragmentItem : public DisplayItemClient {
// Converts the given point, relative to the fragment itself, into a position // Converts the given point, relative to the fragment itself, into a position
// in DOM tree. // in DOM tree.
PositionWithAffinity PositionForPoint(const PhysicalOffset&) const; PositionWithAffinity PositionForPoint(const PhysicalOffset&) const;
PositionWithAffinity PositionForPointInText(
const PhysicalOffset& point,
const NGInlineCursor& cursor) const;
unsigned TextOffsetForPoint(const PhysicalOffset& point,
const NGFragmentItems& items) const;
private: private:
const LayoutBox* InkOverflowOwnerBox() const; const LayoutBox* InkOverflowOwnerBox() const;
......
...@@ -608,12 +608,23 @@ PhysicalOffset NGInlineCursor::LineEndPoint() const { ...@@ -608,12 +608,23 @@ PhysicalOffset NGInlineCursor::LineEndPoint() const {
} }
PositionWithAffinity NGInlineCursor::PositionForPoint( PositionWithAffinity NGInlineCursor::PositionForPoint(
const PhysicalOffset& point) const { const PhysicalOffset& point) {
if (current_paint_fragment_) if (root_paint_fragment_)
return current_paint_fragment_->PositionForPoint(point); return root_paint_fragment_->PositionForPoint(point);
if (current_item_) DCHECK(IsItemCursor());
return current_item_->PositionForPoint(point); while (*this) {
NOTREACHED(); const NGFragmentItem* item = CurrentItem();
DCHECK(item);
// TODO(kojii): Do more staff, when the point is not on any item but within
// line box, etc., see |NGPaintFragment::PositionForPoint|.
if (!item->Rect().Contains(point)) {
MoveToNextItemSkippingChildren();
continue;
}
if (item->Type() == NGFragmentItem::kText)
return item->PositionForPointInText(point, *this);
MoveToNext();
}
return PositionWithAffinity(); return PositionWithAffinity();
} }
......
...@@ -216,8 +216,8 @@ class CORE_EXPORT NGInlineCursor { ...@@ -216,8 +216,8 @@ class CORE_EXPORT NGInlineCursor {
PhysicalOffset LineEndPoint() const; PhysicalOffset LineEndPoint() const;
// Converts the given point, relative to the fragment itself, into a position // Converts the given point, relative to the fragment itself, into a position
// in DOM tree. // in DOM tree within the range of |this|.
PositionWithAffinity PositionForPoint(const PhysicalOffset&) const; PositionWithAffinity PositionForPoint(const PhysicalOffset&);
// //
// Functions to move the current position. // Functions to move the current position.
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include "third_party/blink/renderer/core/layout/hit_test_location.h" #include "third_party/blink/renderer/core/layout/hit_test_location.h"
#include "third_party/blink/renderer/core/layout/layout_analyzer.h" #include "third_party/blink/renderer/core/layout/layout_analyzer.h"
#include "third_party/blink/renderer/core/layout/layout_view.h" #include "third_party/blink/renderer/core/layout/layout_view.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_node_data.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_node_data.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.h"
#include "third_party/blink/renderer/core/layout/ng/layout_box_utils.h" #include "third_party/blink/renderer/core/layout/ng/layout_box_utils.h"
...@@ -361,17 +362,25 @@ PositionWithAffinity LayoutNGBlockFlowMixin<Base>::PositionForPoint( ...@@ -361,17 +362,25 @@ PositionWithAffinity LayoutNGBlockFlowMixin<Base>::PositionForPoint(
if (!Base::ChildrenInline()) if (!Base::ChildrenInline())
return LayoutBlock::PositionForPoint(point); return LayoutBlock::PositionForPoint(point);
if (!PaintFragment()) if (const NGPaintFragment* paint_fragment = PaintFragment()) {
return Base::CreatePositionWithAffinity(0); // The given offset is relative to this |LayoutBlockFlow|. Convert to the
// contents offset.
// The given offset is relative to this |LayoutBlockFlow|. Convert to the PhysicalOffset point_in_contents = point;
// contents offset. Base::OffsetForContents(point_in_contents);
PhysicalOffset point_in_contents = point; if (const PositionWithAffinity position =
Base::OffsetForContents(point_in_contents); paint_fragment->PositionForPoint(point_in_contents))
const PositionWithAffinity ng_position = return position;
PaintFragment()->PositionForPoint(point_in_contents); } else if (const NGFragmentItems* items = Base::FragmentItems()) {
if (ng_position.IsNotNull()) // The given offset is relative to this |LayoutBlockFlow|. Convert to the
return ng_position; // contents offset.
PhysicalOffset point_in_contents = point;
Base::OffsetForContents(point_in_contents);
NGInlineCursor cursor(*items);
if (const PositionWithAffinity position =
cursor.PositionForPoint(point_in_contents))
return position;
}
return Base::CreatePositionWithAffinity(0); return Base::CreatePositionWithAffinity(0);
} }
......
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