Commit 3236b343 authored by Yoshifumi Inoue's avatar Yoshifumi Inoue Committed by Commit Bot

Introduce NGInlineCursor member functions to utilize in NGCartPosition

This patch Introduces |NGInlineCursor| member functions to utilize in
|NGCartPosition|[1]:
 - MoveToFirstLogicalLeaf()
 - MoveToPreviousLine()
 - Property getter
   * HasSoftWrapToNextLine()
   * IsGeneratedText()
   * IsText()
   * CurrentNode()

[1] http://crrev.com/c/1866101 Utilize NGInlineCursor in NGCaretPosition

Bug: 982194
Change-Id: I9f261ebe0fe680ddc98f39a44f3c1c9dd26ef3a5
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1868358
Commit-Queue: Yoshifumi Inoue <yosin@chromium.org>
Commit-Queue: Koji Ishii <kojii@chromium.org>
Reviewed-by: default avatarKoji Ishii <kojii@chromium.org>
Auto-Submit: Yoshifumi Inoue <yosin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#707309}
parent fa7aed86
......@@ -16,6 +16,7 @@ NGFragmentItem::NGFragmentItem(const NGPhysicalTextFragment& text)
type_(kText),
sub_type_(static_cast<unsigned>(text.TextType())),
style_variant_(static_cast<unsigned>(text.StyleVariant())),
is_generated_text_(text.IsGeneratedText()),
is_hidden_for_paint_(false),
text_direction_(static_cast<unsigned>(text.ResolvedDirection())) {
DCHECK_LE(text_.start_offset, text_.end_offset);
......@@ -82,6 +83,13 @@ bool NGFragmentItem::IsAtomicInline() const {
return false;
}
bool NGFragmentItem::IsGeneratedText() const {
if (Type() == kText || Type() == kGeneratedText)
return is_generated_text_;
NOTREACHED();
return false;
}
PhysicalRect NGFragmentItem::SelfInkOverflow() const {
// TODO(kojii): Implement.
return LocalRect();
......
......@@ -238,6 +238,17 @@ class CORE_EXPORT NGFragmentItem : public DisplayItemClient {
return StyleVariant() == NGStyleVariant::kEllipsis;
}
// Returns true if the text is generated (from, e.g., list marker,
// pseudo-element, ...) instead of from a DOM text node.
// * CSS content kText
// * ellipsis kGeneratedText
// * first-letter-part kText
// * list marker kGeneratedText
// * soft hyphen kGeneratedText
// TODO(yosin): When we implement |kGeneratedText|, we rename this function
// to avoid confliction with |kGeneratedText|.
bool IsGeneratedText() const;
bool IsSymbolMarker() const {
return TextType() == NGTextType::kSymbolMarker;
}
......@@ -324,6 +335,10 @@ class CORE_EXPORT NGFragmentItem : public DisplayItemClient {
unsigned type_ : 2; // ItemType
unsigned sub_type_ : 3; // NGTextType
unsigned style_variant_ : 2; // NGStyleVariant
// TODO(yosin): We'll remove |is_generated_text_| field when we construct
// |NGFragmentItem| without |NGPhysicalTextFragment| because usage of this
// varaible, IsGeneratedText(), is not hot.
unsigned is_generated_text_ : 1; // NGPhysicalTextFragment::IsGenerated()
unsigned is_hidden_for_paint_ : 1;
// Note: For |TextItem| and |GeneratedTextItem|, |text_direction_| equals to
// |ShapeResult::Direction()|.
......
......@@ -9,6 +9,7 @@
#include "third_party/blink/renderer/core/layout/ng/inline/ng_physical_line_box_fragment.h"
#include "third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h"
#include "third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h"
#include "third_party/blink/renderer/core/paint/ng/ng_paint_fragment_traversal.h"
namespace blink {
......@@ -115,6 +116,12 @@ bool NGInlineCursor::HasChildren() const {
return false;
}
bool NGInlineCursor::HasSoftWrapToNextLine() const {
DCHECK(IsLineBox());
const NGInlineBreakToken& break_token = CurrentInlineBreakToken();
return !break_token.IsFinished() && !break_token.IsForcedBreak();
}
bool NGInlineCursor::IsAtomicInline() const {
if (current_paint_fragment_)
return current_paint_fragment_->PhysicalFragment().IsAtomicInline();
......@@ -133,6 +140,19 @@ bool NGInlineCursor::IsEllipsis() const {
return false;
}
bool NGInlineCursor::IsGeneratedText() const {
if (current_paint_fragment_) {
if (auto* text_fragment = DynamicTo<NGPhysicalTextFragment>(
current_paint_fragment_->PhysicalFragment()))
return text_fragment->IsGeneratedText();
return false;
}
if (current_item_)
return current_item_->IsGeneratedText();
NOTREACHED();
return false;
}
bool NGInlineCursor::IsHiddenForPaint() const {
if (current_paint_fragment_)
return current_paint_fragment_->PhysicalFragment().IsHiddenForPaint();
......@@ -176,6 +196,17 @@ bool NGInlineCursor::IsLineBreak() const {
return false;
}
bool NGInlineCursor::IsText() const {
if (current_paint_fragment_)
return current_paint_fragment_->PhysicalFragment().IsText();
if (current_item_) {
return current_item_->Type() == NGFragmentItem::kText ||
current_item_->Type() == NGFragmentItem::kGeneratedText;
}
NOTREACHED();
return false;
}
bool NGInlineCursor::IsBeforeSoftLineBreak() const {
if (IsLineBreak())
return false;
......@@ -248,6 +279,18 @@ const NGPhysicalBoxFragment* NGInlineCursor::CurrentBoxFragment() const {
return nullptr;
}
const NGInlineBreakToken& NGInlineCursor::CurrentInlineBreakToken() const {
DCHECK(IsLineBox());
if (current_paint_fragment_) {
return To<NGInlineBreakToken>(
*To<NGPhysicalLineBoxFragment>(
current_paint_fragment_->PhysicalFragment())
.BreakToken());
}
DCHECK(current_item_);
return *current_item_->InlineBreakToken();
}
const LayoutObject* NGInlineCursor::CurrentLayoutObject() const {
if (current_paint_fragment_)
return current_paint_fragment_->GetLayoutObject();
......@@ -257,6 +300,12 @@ const LayoutObject* NGInlineCursor::CurrentLayoutObject() const {
return nullptr;
}
Node* NGInlineCursor::CurrentNode() const {
if (const LayoutObject* layout_object = CurrentLayoutObject())
return layout_object->GetNode();
return nullptr;
}
const PhysicalOffset NGInlineCursor::CurrentOffset() const {
if (current_paint_fragment_)
return current_paint_fragment_->InlineOffsetToContainerBox();
......@@ -368,6 +417,9 @@ void NGInlineCursor::MoveTo(const LayoutObject& layout_object) {
}
void NGInlineCursor::MoveTo(const NGPaintFragment& paint_fragment) {
DCHECK(!fragment_items_);
if (!root_paint_fragment_)
root_paint_fragment_ = paint_fragment.Root();
DCHECK(root_paint_fragment_);
DCHECK(paint_fragment.IsDescendantOfNotSelf(*root_paint_fragment_))
<< paint_fragment << " " << root_paint_fragment_;
......@@ -407,6 +459,21 @@ void NGInlineCursor::MoveToFirstChild() {
MakeNull();
}
void NGInlineCursor::MoveToFirstLogicalLeaf() {
DCHECK(IsLineBox());
// TODO(yosin): This isn't correct for mixed Bidi. Fix it. Besides, we
// should compute and store it during layout.
// TODO(yosin): We should check direction of each container instead of line
// box. See also |NGPhysicalLineBoxFragment::LastLogicalLeaf()|.
if (IsLtr(CurrentStyle().Direction())) {
while (TryToMoveToFirstChild())
continue;
return;
}
while (TryToMoveToLastChild())
continue;
}
void NGInlineCursor::MoveToLastChild() {
DCHECK(CanHaveChildren());
if (!TryToMoveToLastChild())
......@@ -474,6 +541,24 @@ void NGInlineCursor::MoveToNextSkippingChildren() {
MoveToNextItemSkippingChildren();
}
void NGInlineCursor::MoveToPreviousLine() {
DCHECK(IsLineBox());
if (current_paint_fragment_) {
// TODO(yosin): We should implement |PreviousLineOf()| here.
if (auto* paint_fragment =
NGPaintFragmentTraversal::PreviousLineOf(*current_paint_fragment_))
return MoveTo(*paint_fragment);
return MakeNull();
}
if (current_item_) {
do {
MoveToPreviousItem();
} while (IsNotNull() && !IsLineBox());
return;
}
NOTREACHED();
}
bool NGInlineCursor::TryToMoveToFirstChild() {
if (!HasChildren())
return false;
......
......@@ -18,8 +18,10 @@ class LayoutInline;
class LayoutObject;
class NGFragmentItem;
class NGFragmentItems;
class NGInlineBreakToken;
class NGPaintFragment;
class NGPhysicalBoxFragment;
class Node;
struct PhysicalOffset;
struct PhysicalRect;
struct PhysicalSize;
......@@ -73,6 +75,10 @@ class CORE_EXPORT NGInlineCursor {
// True if fragment at the current position has children.
bool HasChildren() const;
// True if current position has soft wrap to next line. It is error to call
// other than line.
bool HasSoftWrapToNextLine() const;
// True if the current position is a atomic inline. It is error to call at
// end.
bool IsAtomicInline() const;
......@@ -84,6 +90,10 @@ class CORE_EXPORT NGInlineCursor {
// True if the current position is an ellipsis. It is error to call at end.
bool IsEllipsis() const;
// True if the current position is a generatd text. It is error to call at
// end.
bool IsGeneratedText() const;
// True if the current position is hidden for paint. It is error to call at
// end.
bool IsHiddenForPaint() const;
......@@ -94,6 +104,9 @@ class CORE_EXPORT NGInlineCursor {
// True if the current position is a line break. It is error to call at end.
bool IsLineBreak() const;
// True if the current position is a text. It is error to call at end.
bool IsText() const;
// |Current*| functions return an object for the current position.
const NGFragmentItem* CurrentItem() const { return current_item_; }
const NGPaintFragment* CurrentPaintFragment() const {
......@@ -104,6 +117,7 @@ class CORE_EXPORT NGInlineCursor {
TextDirection CurrentBaseDirection() const;
const NGPhysicalBoxFragment* CurrentBoxFragment() const;
const LayoutObject* CurrentLayoutObject() const;
Node* CurrentNode() const;
// Returns text direction of current text or atomic inline. It is error to
// call at other than text or atomic inline. Note: <span> doesn't have
// reserved direction.
......@@ -140,6 +154,10 @@ class CORE_EXPORT NGInlineCursor {
// See also |TryToMoveToFirstChild()|.
void MoveToFirstChild();
// Move to first logical leaf of current line box. If current line box has
// no children, curosr becomes null.
void MoveToFirstLogicalLeaf();
// Move to last child of current container box. If the current position is
// at fragment without children, this cursor points nothing.
// See also |TryToMoveToFirstChild()|.
......@@ -162,6 +180,10 @@ class CORE_EXPORT NGInlineCursor {
// Same as |MoveToNext| except that this skips children even if they exist.
void MoveToNextSkippingChildren();
// Move the current position to previous line. It is error to call other than
// line box.
void MoveToPreviousLine();
// Returns true if the current position moves to first child.
bool TryToMoveToFirstChild();
......@@ -174,6 +196,9 @@ class CORE_EXPORT NGInlineCursor {
private:
using ItemsSpan = base::span<const std::unique_ptr<NGFragmentItem>>;
// Returns break token for line box. It is error to call other than line box.
const NGInlineBreakToken& CurrentInlineBreakToken() const;
// True if current position is descendant or self of |layout_object|.
// Note: This function is used for moving cursor in culled inline boxes.
bool IsInclusiveDescendantOf(const LayoutObject& layout_object) const;
......
......@@ -135,6 +135,88 @@ TEST_P(NGInlineCursorTest, FirstChild2) {
EXPECT_FALSE(cursor.TryToMoveToFirstChild());
}
TEST_P(NGInlineCursorTest, FirstLastLogicalLeafInSimpleText) {
// TDOO(yosin): Remove <style> once NGFragmentItem don't do culled inline.
InsertStyleElement("b { background: gray; }");
NGInlineCursor cursor =
SetupCursor("<div id=root><b>first</b><b>middle</b><b>last</b></div>");
NGInlineCursor first_logical_leaf(cursor);
first_logical_leaf.MoveToFirstLogicalLeaf();
EXPECT_EQ("first", ToDebugString(first_logical_leaf));
NGInlineCursor last_logical_leaf(cursor);
last_logical_leaf.MoveToLastLogicalLeaf();
EXPECT_EQ("last", ToDebugString(last_logical_leaf));
}
TEST_P(NGInlineCursorTest, FirstLastLogicalLeafInRtlText) {
// TDOO(yosin): Remove <style> once NGFragmentItem don't do culled inline.
InsertStyleElement("b { background: gray; }");
NGInlineCursor cursor = SetupCursor(
"<bdo id=root dir=rtl style=display:block>"
"<b>first</b><b>middle</b><b>last</b>"
"</bdo>");
NGInlineCursor first_logical_leaf(cursor);
first_logical_leaf.MoveToFirstLogicalLeaf();
EXPECT_EQ("first", ToDebugString(first_logical_leaf));
NGInlineCursor last_logical_leaf(cursor);
last_logical_leaf.MoveToLastLogicalLeaf();
EXPECT_EQ("last", ToDebugString(last_logical_leaf));
}
TEST_P(NGInlineCursorTest, FirstLastLogicalLeafInTextAsDeepDescendants) {
// TDOO(yosin): Remove <style> once NGFragmentItem don't do culled inline.
InsertStyleElement("b { background: gray; }");
NGInlineCursor cursor = SetupCursor(
"<div id=root>"
"<b><b>first</b>ABC</b>"
"<b>middle</b>"
"<b>DEF<b>last</b></b>"
"</div>");
NGInlineCursor first_logical_leaf(cursor);
first_logical_leaf.MoveToFirstLogicalLeaf();
EXPECT_EQ("first", ToDebugString(first_logical_leaf));
NGInlineCursor last_logical_leaf(cursor);
last_logical_leaf.MoveToLastLogicalLeaf();
EXPECT_EQ("last", ToDebugString(last_logical_leaf));
}
TEST_P(NGInlineCursorTest, FirstLastLogicalLeafWithInlineBlock) {
InsertStyleElement("b { display: inline-block; }");
NGInlineCursor cursor = SetupCursor(
"<div id=root>"
"<b id=first>first</b>middle<b id=last>last</b>"
"</div>");
NGInlineCursor first_logical_leaf(cursor);
first_logical_leaf.MoveToFirstLogicalLeaf();
EXPECT_EQ("#first", ToDebugString(first_logical_leaf))
<< "stop at inline-block";
NGInlineCursor last_logical_leaf(cursor);
last_logical_leaf.MoveToLastLogicalLeaf();
EXPECT_EQ("#last", ToDebugString(last_logical_leaf))
<< "stop at inline-block";
}
TEST_P(NGInlineCursorTest, FirstLastLogicalLeafWithImages) {
NGInlineCursor cursor =
SetupCursor("<div id=root><img id=first>middle<img id=last></div>");
NGInlineCursor first_logical_leaf(cursor);
first_logical_leaf.MoveToFirstLogicalLeaf();
EXPECT_EQ("#first", ToDebugString(first_logical_leaf));
NGInlineCursor last_logical_leaf(cursor);
last_logical_leaf.MoveToLastLogicalLeaf();
EXPECT_EQ("#last", ToDebugString(last_logical_leaf));
}
TEST_P(NGInlineCursorTest, LastChild) {
// TDOO(yosin): Remove <style> once NGFragmentItem don't do culled inline.
InsertStyleElement("a, b { background: gray; }");
......@@ -285,4 +367,25 @@ TEST_P(NGInlineCursorTest, EmptyOutOfFlow) {
EXPECT_THAT(list, ElementsAre());
}
TEST_P(NGInlineCursorTest, PreviousLine) {
NGInlineCursor cursor = SetupCursor("<div id=root>abc<br>xyz</div>");
NGInlineCursor line1(cursor);
while (line1 && !line1.IsLineBox())
line1.MoveToNext();
ASSERT_TRUE(line1.IsNotNull());
NGInlineCursor line2(line1);
line2.MoveToNext();
while (line2 && !line2.IsLineBox())
line2.MoveToNext();
ASSERT_NE(line1, line2);
NGInlineCursor should_be_null(line1);
should_be_null.MoveToPreviousLine();
EXPECT_TRUE(should_be_null.IsNull());
NGInlineCursor should_be_line1(line2);
should_be_line1.MoveToPreviousLine();
EXPECT_EQ(line1, should_be_line1);
}
} // namespace blink
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