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(
DCHECK(line_box.IsLineBox()) << line_box;
const PhysicalOffset start_point = line_box.LineStartPoint();
return FromPositionInDOMTree<Strategy>(
line_box.PositionForPoint(start_point));
line_box.CursorForDescendants().PositionForPoint(start_point));
}
const InlineBox* inline_box =
......@@ -254,7 +254,7 @@ static PositionWithAffinityTemplate<Strategy> EndPositionForLine(
line_box.MoveToContainingLine();
const PhysicalOffset end_point = line_box.LineEndPoint();
return FromPositionInDOMTree<Strategy>(
line_box.PositionForPoint(end_point));
line_box.CursorForDescendants().PositionForPoint(end_point));
}
const InlineBox* inline_box =
......
......@@ -4,7 +4,9 @@
#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/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_inline_cursor.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) {
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(
const LayoutObject& layout_object) {
DCHECK(layout_object.IsInLayoutNGInlineFormattingContext());
......
......@@ -339,6 +339,11 @@ class CORE_EXPORT NGFragmentItem : public DisplayItemClient {
// Converts the given point, relative to the fragment itself, into a position
// in DOM tree.
PositionWithAffinity PositionForPoint(const PhysicalOffset&) const;
PositionWithAffinity PositionForPointInText(
const PhysicalOffset& point,
const NGInlineCursor& cursor) const;
unsigned TextOffsetForPoint(const PhysicalOffset& point,
const NGFragmentItems& items) const;
private:
const LayoutBox* InkOverflowOwnerBox() const;
......
......@@ -608,12 +608,23 @@ PhysicalOffset NGInlineCursor::LineEndPoint() const {
}
PositionWithAffinity NGInlineCursor::PositionForPoint(
const PhysicalOffset& point) const {
if (current_paint_fragment_)
return current_paint_fragment_->PositionForPoint(point);
if (current_item_)
return current_item_->PositionForPoint(point);
NOTREACHED();
const PhysicalOffset& point) {
if (root_paint_fragment_)
return root_paint_fragment_->PositionForPoint(point);
DCHECK(IsItemCursor());
while (*this) {
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();
}
......
......@@ -216,8 +216,8 @@ class CORE_EXPORT NGInlineCursor {
PhysicalOffset LineEndPoint() const;
// Converts the given point, relative to the fragment itself, into a position
// in DOM tree.
PositionWithAffinity PositionForPoint(const PhysicalOffset&) const;
// in DOM tree within the range of |this|.
PositionWithAffinity PositionForPoint(const PhysicalOffset&);
//
// Functions to move the current position.
......
......@@ -11,6 +11,7 @@
#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_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_physical_line_box_fragment.h"
#include "third_party/blink/renderer/core/layout/ng/layout_box_utils.h"
......@@ -361,17 +362,25 @@ PositionWithAffinity LayoutNGBlockFlowMixin<Base>::PositionForPoint(
if (!Base::ChildrenInline())
return LayoutBlock::PositionForPoint(point);
if (!PaintFragment())
return Base::CreatePositionWithAffinity(0);
// The given offset is relative to this |LayoutBlockFlow|. Convert to the
// contents offset.
PhysicalOffset point_in_contents = point;
Base::OffsetForContents(point_in_contents);
const PositionWithAffinity ng_position =
PaintFragment()->PositionForPoint(point_in_contents);
if (ng_position.IsNotNull())
return ng_position;
if (const NGPaintFragment* paint_fragment = PaintFragment()) {
// The given offset is relative to this |LayoutBlockFlow|. Convert to the
// contents offset.
PhysicalOffset point_in_contents = point;
Base::OffsetForContents(point_in_contents);
if (const PositionWithAffinity position =
paint_fragment->PositionForPoint(point_in_contents))
return position;
} else if (const NGFragmentItems* items = Base::FragmentItems()) {
// The given offset is relative to this |LayoutBlockFlow|. Convert to the
// 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);
}
......
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