Commit 92f0dff9 authored by Yoshifumi Inoue's avatar Yoshifumi Inoue Committed by Commit Bot

Paint selection with NGFragmentItem

This patch implements |NGFragmentItem| version of selection painting by using
|NGInlineCursor| to share logic with |NGPaintFragment| version.

Bug: 982194
Change-Id: I44bd0e1b93db611fb03459179dda0bf5d350167d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1832843
Commit-Queue: Yoshifumi Inoue <yosin@chromium.org>
Reviewed-by: default avatarKoji Ishii <kojii@chromium.org>
Auto-Submit: Yoshifumi Inoue <yosin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#704984}
parent 4540534e
...@@ -1246,8 +1246,8 @@ void FrameSelection::ClearDocumentCachedRange() { ...@@ -1246,8 +1246,8 @@ void FrameSelection::ClearDocumentCachedRange() {
} }
LayoutSelectionStatus FrameSelection::ComputeLayoutSelectionStatus( LayoutSelectionStatus FrameSelection::ComputeLayoutSelectionStatus(
const NGPaintFragment& text_fragment) const { const NGInlineCursor& cursor) const {
return layout_selection_->ComputeSelectionStatus(text_fragment); return layout_selection_->ComputeSelectionStatus(cursor);
} }
bool FrameSelection::IsDirectional() const { bool FrameSelection::IsDirectional() const {
......
...@@ -50,7 +50,7 @@ class LocalFrame; ...@@ -50,7 +50,7 @@ class LocalFrame;
class FrameCaret; class FrameCaret;
class GranularityStrategy; class GranularityStrategy;
class GraphicsContext; class GraphicsContext;
class NGPaintFragment; class NGInlineCursor;
class Range; class Range;
class SelectionEditor; class SelectionEditor;
class LayoutSelection; class LayoutSelection;
...@@ -69,7 +69,7 @@ enum class CaretVisibility; ...@@ -69,7 +69,7 @@ enum class CaretVisibility;
enum class HandleVisibility { kNotVisible, kVisible }; enum class HandleVisibility { kNotVisible, kVisible };
enum class SelectSoftLineBreak { kNotSelected, kSelected }; enum class SelectSoftLineBreak { kNotSelected, kSelected };
// This is return type of ComputeLayoutSelectionStatus(paintfragment). // This is return type of ComputeLayoutSelectionStatus(cursor).
// This structure represents how the fragment is selected. // This structure represents how the fragment is selected.
// |start|, |end| : Selection start/end offset. This offset is based on // |start|, |end| : Selection start/end offset. This offset is based on
// the text of NGInlineNode of a parent block thus // the text of NGInlineNode of a parent block thus
...@@ -281,7 +281,7 @@ class CORE_EXPORT FrameSelection final ...@@ -281,7 +281,7 @@ class CORE_EXPORT FrameSelection final
LayoutTextSelectionStatus ComputeLayoutSelectionStatus( LayoutTextSelectionStatus ComputeLayoutSelectionStatus(
const LayoutText& text) const; const LayoutText& text) const;
LayoutSelectionStatus ComputeLayoutSelectionStatus( LayoutSelectionStatus ComputeLayoutSelectionStatus(
const NGPaintFragment&) const; const NGInlineCursor& cursor) const;
void Trace(Visitor*) override; void Trace(Visitor*) override;
......
...@@ -33,11 +33,12 @@ ...@@ -33,11 +33,12 @@
#include "third_party/blink/renderer/core/layout/layout_text.h" #include "third_party/blink/renderer/core/layout/layout_text.h"
#include "third_party/blink/renderer/core/layout/layout_text_fragment.h" #include "third_party/blink/renderer/core/layout/layout_text_fragment.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_fragment_item.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping.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/inline/ng_physical_text_fragment.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.h"
#include "third_party/blink/renderer/core/layout/ng/ng_block_node.h" #include "third_party/blink/renderer/core/layout/ng/ng_block_node.h"
#include "third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h"
#include "third_party/blink/renderer/core/paint/paint_layer.h" #include "third_party/blink/renderer/core/paint/paint_layer.h"
#include "third_party/blink/renderer/platform/fonts/shaping/shape_result_view.h" #include "third_party/blink/renderer/platform/fonts/shaping/shape_result_view.h"
...@@ -118,6 +119,13 @@ enum class SelectionMode { ...@@ -118,6 +119,13 @@ enum class SelectionMode {
kBlockCursor, kBlockCursor,
}; };
void LayoutSelection::AssertIsValid() const {
const Document& document = frame_selection_->GetDocument();
DCHECK_GE(document.Lifecycle().GetState(), DocumentLifecycle::kLayoutClean);
DCHECK(!document.IsSlotAssignmentOrLegacyDistributionDirty());
DCHECK(!has_pending_selection_);
}
static SelectionMode ComputeSelectionMode( static SelectionMode ComputeSelectionMode(
const FrameSelection& frame_selection) { const FrameSelection& frame_selection) {
const SelectionInDOMTree& selection_in_dom = const SelectionInDOMTree& selection_in_dom =
...@@ -549,49 +557,11 @@ static SelectionPaintRange* ComputeNewPaintRange( ...@@ -549,49 +557,11 @@ static SelectionPaintRange* ComputeNewPaintRange(
*paint_range.start_node, start_offset, *paint_range.end_node, end_offset); *paint_range.start_node, start_offset, *paint_range.end_node, end_offset);
} }
// ClampOffset modifies |offset| fixed in a range of |text_fragment| start/end
// offsets.
static unsigned ClampOffset(unsigned offset, static unsigned ClampOffset(unsigned offset,
const NGPhysicalTextFragment& text_fragment) { unsigned start_offset,
return std::min(std::max(offset, text_fragment.StartOffset()), unsigned end_offset) {
text_fragment.EndOffset()); DCHECK_LE(start_offset, end_offset);
} return std::min(std::max(offset, start_offset), end_offset);
// We don't paint a line break the end of inline-block
// because if an inline-block is at the middle of line, we should not paint
// a line break.
// Old layout paints line break if the inline-block is at the end of line, but
// since its complex to determine if the inline-block is at the end of line on NG,
// we just cancels block-end line break painting for any inline-block.
static bool IsLastLineInInlineBlock(const NGPaintFragment& line) {
DCHECK(line.PhysicalFragment().IsLineBox());
NGPaintFragment* parent = line.Parent();
if (!parent->PhysicalFragment().IsAtomicInline())
return false;
return &parent->Children().back() == &line;
}
static bool IsBeforeSoftLineBreak(const NGPaintFragment& fragment) {
if (To<NGPhysicalTextFragment>(fragment.PhysicalFragment()).IsLineBreak())
return false;
// TODO(yoichio): InlineBlock should not be container line box.
// See paint/selection/text-selection-inline-block.html.
const NGPaintFragment* container_line_box = fragment.ContainerLineBox();
DCHECK(container_line_box);
if (IsLastLineInInlineBlock(*container_line_box))
return false;
const auto& physical_line_box =
To<NGPhysicalLineBoxFragment>(container_line_box->PhysicalFragment());
const NGPhysicalFragment* last_leaf = physical_line_box.LastLogicalLeaf();
DCHECK(last_leaf);
if (&fragment.PhysicalFragment() != last_leaf)
return false;
// Even If |fragment| is before linebreak, if its direction differs to line
// direction, we don't paint line break. See
// paint/selection/text-selection-newline-mixed-ltr-rtl.html.
return physical_line_box.BaseDirection() ==
fragment.PhysicalFragment().ResolvedDirection();
} }
static Text* AssociatedTextNode(const LayoutText& text) { static Text* AssociatedTextNode(const LayoutText& text) {
...@@ -613,6 +583,12 @@ static SelectionState GetSelectionStateFor(const LayoutText& layout_text) { ...@@ -613,6 +583,12 @@ static SelectionState GetSelectionStateFor(const LayoutText& layout_text) {
return layout_text.GetSelectionState(); return layout_text.GetSelectionState();
} }
static SelectionState GetSelectionStateFor(const NGInlineCursor& cursor) {
DCHECK(cursor.CurrentLayoutObject() &&
cursor.CurrentLayoutObject()->IsText());
return GetSelectionStateFor(ToLayoutText(*cursor.CurrentLayoutObject()));
}
bool LayoutSelection::IsSelected(const LayoutObject& layout_object) { bool LayoutSelection::IsSelected(const LayoutObject& layout_object) {
if (const LayoutText* layout_text = ToLayoutTextOrNull(layout_object)) if (const LayoutText* layout_text = ToLayoutTextOrNull(layout_object))
return GetSelectionStateFor(*layout_text) != SelectionState::kNone; return GetSelectionStateFor(*layout_text) != SelectionState::kNone;
...@@ -650,10 +626,7 @@ static LayoutTextSelectionStatus ComputeSelectionStatusForNode( ...@@ -650,10 +626,7 @@ static LayoutTextSelectionStatus ComputeSelectionStatusForNode(
LayoutTextSelectionStatus LayoutSelection::ComputeSelectionStatus( LayoutTextSelectionStatus LayoutSelection::ComputeSelectionStatus(
const LayoutText& layout_text) const { const LayoutText& layout_text) const {
Document& document = frame_selection_->GetDocument(); AssertIsValid();
DCHECK_GE(document.Lifecycle().GetState(), DocumentLifecycle::kLayoutClean);
DCHECK(!document.IsSlotAssignmentOrLegacyDistributionDirty());
DCHECK(!has_pending_selection_);
const SelectionState selection_state = GetSelectionStateFor(layout_text); const SelectionState selection_state = GetSelectionStateFor(layout_text);
if (selection_state == SelectionState::kNone) if (selection_state == SelectionState::kNone)
return {0, 0, SelectionIncludeEnd::kNotInclude}; return {0, 0, SelectionIncludeEnd::kNotInclude};
...@@ -683,54 +656,50 @@ LayoutTextSelectionStatus FrameSelection::ComputeLayoutSelectionStatus( ...@@ -683,54 +656,50 @@ LayoutTextSelectionStatus FrameSelection::ComputeLayoutSelectionStatus(
// FrameSelection holds selection offsets in layout block flow at // FrameSelection holds selection offsets in layout block flow at
// LayoutSelection::Commit() if selection starts/ends within Text that // LayoutSelection::Commit() if selection starts/ends within Text that
// each LayoutObject::SelectionState indicates. // each LayoutObject::SelectionState indicates.
// These offset can be out of |text_fragment| because SelectionState is of each // These offset can be out of fragment because SelectionState is of each
// LayoutText and not of each NGPhysicalTextFragment for it. // LayoutText and not of each fragment for it.
LayoutSelectionStatus LayoutSelection::ComputeSelectionStatus( LayoutSelectionStatus LayoutSelection::ComputeSelectionStatus(
const NGPaintFragment& fragment) const { const NGInlineCursor& cursor) const {
Document& document = frame_selection_->GetDocument();
DCHECK_GE(document.Lifecycle().GetState(), DocumentLifecycle::kLayoutClean);
DCHECK(!document.IsSlotAssignmentOrLegacyDistributionDirty());
const auto& text_fragment =
To<NGPhysicalTextFragment>(fragment.PhysicalFragment());
// We don't paint selection on ellipsis. // We don't paint selection on ellipsis.
if (text_fragment.StyleVariant() == NGStyleVariant::kEllipsis) if (cursor.IsEllipsis())
return {0, 0, SelectSoftLineBreak::kNotSelected}; return {0, 0, SelectSoftLineBreak::kNotSelected};
// Needs GetSelectionStateFor const unsigned start_offset = cursor.CurrentTextStartOffset();
DCHECK(text_fragment.GetLayoutObject()); const unsigned end_offset = cursor.CurrentTextEndOffset();
switch ( switch (GetSelectionStateFor(cursor)) {
GetSelectionStateFor(ToLayoutText(*text_fragment.GetLayoutObject()))) {
case SelectionState::kStart: { case SelectionState::kStart: {
const unsigned start_in_block = paint_range_->start_offset.value(); const unsigned start_in_block = paint_range_->start_offset.value();
const bool is_continuous = start_in_block <= text_fragment.EndOffset(); const bool is_continuous = start_in_block <= end_offset;
return {ClampOffset(start_in_block, text_fragment), return {ClampOffset(start_in_block, start_offset, end_offset), end_offset,
text_fragment.EndOffset(), (is_continuous && cursor.IsBeforeSoftLineBreak())
(is_continuous && IsBeforeSoftLineBreak(fragment))
? SelectSoftLineBreak::kSelected ? SelectSoftLineBreak::kSelected
: SelectSoftLineBreak::kNotSelected}; : SelectSoftLineBreak::kNotSelected};
} }
case SelectionState::kEnd: { case SelectionState::kEnd: {
const unsigned end_in_block = paint_range_->end_offset.value(); const unsigned end_in_block = paint_range_->end_offset.value();
const unsigned end_in_fragment = ClampOffset(end_in_block, text_fragment); const unsigned end_in_fragment =
const bool is_continuous = text_fragment.EndOffset() < end_in_block; ClampOffset(end_in_block, start_offset, end_offset);
return {text_fragment.StartOffset(), end_in_fragment, const bool is_continuous = end_offset < end_in_block;
(is_continuous && IsBeforeSoftLineBreak(fragment)) return {start_offset, end_in_fragment,
(is_continuous && cursor.IsBeforeSoftLineBreak())
? SelectSoftLineBreak::kSelected ? SelectSoftLineBreak::kSelected
: SelectSoftLineBreak::kNotSelected}; : SelectSoftLineBreak::kNotSelected};
} }
case SelectionState::kStartAndEnd: { case SelectionState::kStartAndEnd: {
const unsigned start_in_block = paint_range_->start_offset.value(); const unsigned start_in_block = paint_range_->start_offset.value();
const unsigned end_in_block = paint_range_->end_offset.value(); const unsigned end_in_block = paint_range_->end_offset.value();
const unsigned end_in_fragment = ClampOffset(end_in_block, text_fragment); const unsigned end_in_fragment =
const bool is_continuous = start_in_block <= text_fragment.EndOffset() && ClampOffset(end_in_block, start_offset, end_offset);
text_fragment.EndOffset() < end_in_block; const bool is_continuous =
return {ClampOffset(start_in_block, text_fragment), end_in_fragment, start_in_block <= end_offset && end_offset < end_in_block;
(is_continuous && IsBeforeSoftLineBreak(fragment)) return {ClampOffset(start_in_block, start_offset, end_offset),
end_in_fragment,
(is_continuous && cursor.IsBeforeSoftLineBreak())
? SelectSoftLineBreak::kSelected ? SelectSoftLineBreak::kSelected
: SelectSoftLineBreak::kNotSelected}; : SelectSoftLineBreak::kNotSelected};
} }
case SelectionState::kInside: { case SelectionState::kInside: {
return {text_fragment.StartOffset(), text_fragment.EndOffset(), return {start_offset, end_offset,
IsBeforeSoftLineBreak(fragment) cursor.IsBeforeSoftLineBreak()
? SelectSoftLineBreak::kSelected ? SelectSoftLineBreak::kSelected
: SelectSoftLineBreak::kNotSelected}; : SelectSoftLineBreak::kNotSelected};
} }
......
...@@ -32,7 +32,7 @@ namespace blink { ...@@ -32,7 +32,7 @@ namespace blink {
class IntRect; class IntRect;
class LayoutObject; class LayoutObject;
class LayoutText; class LayoutText;
class NGPaintFragment; class NGInlineCursor;
class FrameSelection; class FrameSelection;
struct LayoutSelectionStatus; struct LayoutSelectionStatus;
struct LayoutTextSelectionStatus; struct LayoutTextSelectionStatus;
...@@ -49,7 +49,7 @@ class LayoutSelection final : public GarbageCollected<LayoutSelection> { ...@@ -49,7 +49,7 @@ class LayoutSelection final : public GarbageCollected<LayoutSelection> {
void InvalidatePaintForSelection(); void InvalidatePaintForSelection();
LayoutTextSelectionStatus ComputeSelectionStatus(const LayoutText&) const; LayoutTextSelectionStatus ComputeSelectionStatus(const LayoutText&) const;
LayoutSelectionStatus ComputeSelectionStatus(const NGPaintFragment&) const; LayoutSelectionStatus ComputeSelectionStatus(const NGInlineCursor&) const;
static bool IsSelected(const LayoutObject&); static bool IsSelected(const LayoutObject&);
void OnDocumentShutdown(); void OnDocumentShutdown();
...@@ -57,6 +57,8 @@ class LayoutSelection final : public GarbageCollected<LayoutSelection> { ...@@ -57,6 +57,8 @@ class LayoutSelection final : public GarbageCollected<LayoutSelection> {
void Trace(Visitor*); void Trace(Visitor*);
private: private:
void AssertIsValid() const;
Member<FrameSelection> frame_selection_; Member<FrameSelection> frame_selection_;
bool has_pending_selection_ : 1; bool has_pending_selection_ : 1;
......
...@@ -12,9 +12,7 @@ ...@@ -12,9 +12,7 @@
#include "third_party/blink/renderer/core/layout/layout_object.h" #include "third_party/blink/renderer/core/layout/layout_object.h"
#include "third_party/blink/renderer/core/layout/layout_text.h" #include "third_party/blink/renderer/core/layout/layout_text.h"
#include "third_party/blink/renderer/core/layout/layout_text_fragment.h" #include "third_party/blink/renderer/core/layout/layout_text_fragment.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_cursor.h"
#include "third_party/blink/renderer/core/layout/ng/layout_ng_block_flow.h"
#include "third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h"
#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h" #include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
#include "third_party/blink/renderer/platform/wtf/assertions.h" #include "third_party/blink/renderer/platform/wtf/assertions.h"
#include "third_party/blink/renderer/platform/wtf/functional.h" #include "third_party/blink/renderer/platform/wtf/functional.h"
...@@ -40,14 +38,15 @@ class LayoutSelectionTestBase : public EditingTestBase { ...@@ -40,14 +38,15 @@ class LayoutSelectionTestBase : public EditingTestBase {
std::ostream& ostream, std::ostream& ostream,
const LayoutText& layout_text, const LayoutText& layout_text,
SelectionState state) { SelectionState state) {
const auto fragments = NGPaintFragment::InlineFragmentsFor(&layout_text); if (layout_text.IsInLayoutNGInlineFormattingContext()) {
if (fragments.IsInLayoutNGInlineFormattingContext()) { NGInlineCursor cursor(*layout_text.RootInlineFormattingContext());
const unsigned text_start = cursor.MoveTo(layout_text);
To<NGPhysicalTextFragment>(fragments.begin()->PhysicalFragment()) if (!cursor)
.StartOffset(); return;
for (const NGPaintFragment* fragment : fragments) { const unsigned text_start = cursor.CurrentTextStartOffset();
for (; cursor; cursor.MoveToNextForSameLayoutObject()) {
const LayoutSelectionStatus status = const LayoutSelectionStatus status =
selection.ComputeLayoutSelectionStatus(*fragment); selection.ComputeLayoutSelectionStatus(cursor);
if (state == SelectionState::kNone && status.start == status.end) if (state == SelectionState::kNone && status.start == status.end)
continue; continue;
ostream << "(" << status.start - text_start << "," ostream << "(" << status.start - text_start << ","
...@@ -944,32 +943,6 @@ TEST_P(LayoutSelectionTest, InvalidateSlot) { ...@@ -944,32 +943,6 @@ TEST_P(LayoutSelectionTest, InvalidateSlot) {
DumpSelectionInfo()); DumpSelectionInfo());
} }
static const NGPaintFragment* FindNGPaintFragmentInternal(
const NGPaintFragment* paint,
const LayoutObject* layout_object) {
if (paint->GetLayoutObject() == layout_object)
return paint;
for (const NGPaintFragment* child : paint->Children()) {
if (const NGPaintFragment* child_fragment =
FindNGPaintFragmentInternal(child, layout_object))
return child_fragment;
}
return nullptr;
}
static const NGPaintFragment& GetNGPaintFragment(
const LayoutObject* layout_object) {
DCHECK(layout_object->IsText());
LayoutBlockFlow* block_flow = layout_object->ContainingNGBlockFlow();
DCHECK(block_flow);
DCHECK(block_flow->IsLayoutNGMixin());
LayoutNGBlockFlow* layout_ng = ToLayoutNGBlockFlow(block_flow);
const NGPaintFragment* paint_fragment =
FindNGPaintFragmentInternal(layout_ng->PaintFragment(), layout_object);
DCHECK(paint_fragment);
return *paint_fragment;
}
class NGLayoutSelectionTest class NGLayoutSelectionTest
: public LayoutSelectionTestBase, : public LayoutSelectionTestBase,
private ScopedLayoutNGForTest, private ScopedLayoutNGForTest,
...@@ -979,6 +952,7 @@ class NGLayoutSelectionTest ...@@ -979,6 +952,7 @@ class NGLayoutSelectionTest
: ScopedLayoutNGForTest(true), : ScopedLayoutNGForTest(true),
ScopedPaintUnderInvalidationCheckingForTest(true) {} ScopedPaintUnderInvalidationCheckingForTest(true) {}
protected:
const Text* GetFirstTextNode() { const Text* GetFirstTextNode() {
for (const Node& runner : NodeTraversal::StartsAt(*GetDocument().body())) { for (const Node& runner : NodeTraversal::StartsAt(*GetDocument().body())) {
if (auto* text_node = DynamicTo<Text>(runner)) if (auto* text_node = DynamicTo<Text>(runner))
...@@ -990,17 +964,22 @@ class NGLayoutSelectionTest ...@@ -990,17 +964,22 @@ class NGLayoutSelectionTest
bool IsFirstTextLineBreak(const std::string& selection_text) { bool IsFirstTextLineBreak(const std::string& selection_text) {
SetSelectionAndUpdateLayoutSelection(selection_text); SetSelectionAndUpdateLayoutSelection(selection_text);
const Text* const first_text = GetFirstTextNode(); const LayoutText& first_text = *GetFirstTextNode()->GetLayoutObject();
const NGPaintFragment& fragment =
GetNGPaintFragment(first_text->GetLayoutObject());
const LayoutSelectionStatus& status = const LayoutSelectionStatus& status =
Selection().ComputeLayoutSelectionStatus(fragment); ComputeLayoutSelectionStatus(first_text);
return status.line_break == SelectSoftLineBreak::kSelected; return status.line_break == SelectSoftLineBreak::kSelected;
} }
LayoutSelectionStatus ComputeLayoutSelectionStatus(const Node& node) { LayoutSelectionStatus ComputeLayoutSelectionStatus(const Node& node) {
return Selection().ComputeLayoutSelectionStatus( return ComputeLayoutSelectionStatus(*node.GetLayoutObject());
GetNGPaintFragment(node.GetLayoutObject())); }
LayoutSelectionStatus ComputeLayoutSelectionStatus(
const LayoutObject& layout_object) const {
DCHECK(layout_object.IsText());
NGInlineCursor cursor(*layout_object.RootInlineFormattingContext());
cursor.MoveTo(layout_object);
return Selection().ComputeLayoutSelectionStatus(cursor);
} }
void SetSelectionAndUpdateLayoutSelection(const std::string& selection_text) { void SetSelectionAndUpdateLayoutSelection(const std::string& selection_text) {
...@@ -1053,7 +1032,7 @@ TEST_F(NGLayoutSelectionTest, TwoNGBlockFlows) { ...@@ -1053,7 +1032,7 @@ TEST_F(NGLayoutSelectionTest, TwoNGBlockFlows) {
LayoutObject* const foo = LayoutObject* const foo =
GetDocument().body()->firstChild()->firstChild()->GetLayoutObject(); GetDocument().body()->firstChild()->firstChild()->GetLayoutObject();
EXPECT_EQ(LayoutSelectionStatus(1u, 3u, SelectSoftLineBreak::kSelected), EXPECT_EQ(LayoutSelectionStatus(1u, 3u, SelectSoftLineBreak::kSelected),
Selection().ComputeLayoutSelectionStatus(GetNGPaintFragment(foo))); ComputeLayoutSelectionStatus(*foo));
LayoutObject* const bar = GetDocument() LayoutObject* const bar = GetDocument()
.body() .body()
->firstChild() ->firstChild()
...@@ -1061,7 +1040,7 @@ TEST_F(NGLayoutSelectionTest, TwoNGBlockFlows) { ...@@ -1061,7 +1040,7 @@ TEST_F(NGLayoutSelectionTest, TwoNGBlockFlows) {
->firstChild() ->firstChild()
->GetLayoutObject(); ->GetLayoutObject();
EXPECT_EQ(LayoutSelectionStatus(0u, 2u, SelectSoftLineBreak::kNotSelected), EXPECT_EQ(LayoutSelectionStatus(0u, 2u, SelectSoftLineBreak::kNotSelected),
Selection().ComputeLayoutSelectionStatus(GetNGPaintFragment(bar))); ComputeLayoutSelectionStatus(*bar));
} }
// TODO(editing-dev): Once LayoutNG supports editing, we should change this // TODO(editing-dev): Once LayoutNG supports editing, we should change this
...@@ -1123,15 +1102,14 @@ TEST_F(NGLayoutSelectionTest, LineBreakBasic) { ...@@ -1123,15 +1102,14 @@ TEST_F(NGLayoutSelectionTest, LineBreakBasic) {
EXPECT_FALSE(IsFirstTextLineBreak("<div>f^oo|</div>")); EXPECT_FALSE(IsFirstTextLineBreak("<div>f^oo|</div>"));
EXPECT_FALSE(IsFirstTextLineBreak("<div>f^oo<!--|--></div>")); EXPECT_FALSE(IsFirstTextLineBreak("<div>f^oo<!--|--></div>"));
EXPECT_FALSE(IsFirstTextLineBreak("<div>f^oo</div>|")); EXPECT_FALSE(IsFirstTextLineBreak("<div>f^oo</div>|"));
// TODO(yoichio): Fix the test. See LayoutSelection::IsLineBreak.
// EXPECT_FALSE(IsFirstTextLineBreak(
// "<div style='display:inline-block'>f^oo</div>bar|"));
} }
TEST_F(NGLayoutSelectionTest, LineBreakInlineBlock) { TEST_F(NGLayoutSelectionTest, LineBreakInlineBlock) {
LoadAhem(); LoadAhem();
EXPECT_FALSE( EXPECT_FALSE(
IsFirstTextLineBreak("<div style='display:inline-block'>^x</div>y|")); IsFirstTextLineBreak("<div style='display:inline-block'>^x</div>y|"));
EXPECT_FALSE(
IsFirstTextLineBreak("<div style='display:inline-block'>f^oo</div>bar|"));
} }
TEST_F(NGLayoutSelectionTest, LineBreakImage) { TEST_F(NGLayoutSelectionTest, LineBreakImage) {
...@@ -1155,9 +1133,8 @@ TEST_F(NGLayoutSelectionTest, BRStatus) { ...@@ -1155,9 +1133,8 @@ TEST_F(NGLayoutSelectionTest, BRStatus) {
LayoutObject* const layout_br = LayoutObject* const layout_br =
GetDocument().QuerySelector("br")->GetLayoutObject(); GetDocument().QuerySelector("br")->GetLayoutObject();
CHECK(layout_br->IsBR()); CHECK(layout_br->IsBR());
EXPECT_EQ( EXPECT_EQ(LayoutSelectionStatus(3u, 4u, SelectSoftLineBreak::kNotSelected),
LayoutSelectionStatus(3u, 4u, SelectSoftLineBreak::kNotSelected), ComputeLayoutSelectionStatus(*layout_br));
Selection().ComputeLayoutSelectionStatus(GetNGPaintFragment(layout_br)));
} }
// https://crbug.com/907186 // https://crbug.com/907186
...@@ -1166,9 +1143,8 @@ TEST_F(NGLayoutSelectionTest, WBRStatus) { ...@@ -1166,9 +1143,8 @@ TEST_F(NGLayoutSelectionTest, WBRStatus) {
"<div style=\"width:0\">^foo<wbr>bar|</div>"); "<div style=\"width:0\">^foo<wbr>bar|</div>");
const LayoutObject* layout_wbr = const LayoutObject* layout_wbr =
GetDocument().QuerySelector("wbr")->GetLayoutObject(); GetDocument().QuerySelector("wbr")->GetLayoutObject();
EXPECT_EQ( EXPECT_EQ(LayoutSelectionStatus(3u, 4u, SelectSoftLineBreak::kSelected),
LayoutSelectionStatus(3u, 4u, SelectSoftLineBreak::kSelected), ComputeLayoutSelectionStatus(*layout_wbr));
Selection().ComputeLayoutSelectionStatus(GetNGPaintFragment(layout_wbr)));
} }
} // namespace blink } // namespace blink
...@@ -54,6 +54,7 @@ ...@@ -54,6 +54,7 @@
#include "third_party/blink/renderer/core/layout/ng/inline/layout_ng_text.h" #include "third_party/blink/renderer/core/layout/ng/inline/layout_ng_text.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_abstract_inline_text_box.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_abstract_inline_text_box.h"
#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/layout/ng/inline/ng_inline_cursor.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_fragment_traversal.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_fragment_traversal.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping.h" #include "third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping.h"
...@@ -2084,20 +2085,19 @@ PhysicalRect LayoutText::LocalSelectionVisualRect() const { ...@@ -2084,20 +2085,19 @@ PhysicalRect LayoutText::LocalSelectionVisualRect() const {
return PhysicalRect(); return PhysicalRect();
const FrameSelection& frame_selection = GetFrame()->Selection(); const FrameSelection& frame_selection = GetFrame()->Selection();
const auto fragments = NGPaintFragment::InlineFragmentsFor(this); if (IsInLayoutNGInlineFormattingContext()) {
if (fragments.IsInLayoutNGInlineFormattingContext()) {
PhysicalRect rect; PhysicalRect rect;
for (const NGPaintFragment* fragment : fragments) { NGInlineCursor cursor(*RootInlineFormattingContext());
if (fragment->PhysicalFragment().IsHiddenForPaint()) for (cursor.MoveTo(*this); cursor; cursor.MoveToNextForSameLayoutObject()) {
if (cursor.IsHiddenForPaint())
continue; continue;
const LayoutSelectionStatus status = const LayoutSelectionStatus status =
frame_selection.ComputeLayoutSelectionStatus(*fragment); frame_selection.ComputeLayoutSelectionStatus(cursor);
if (status.start == status.end) if (status.start == status.end)
continue; continue;
PhysicalRect fragment_rect = PhysicalRect item_rect = ComputeLocalSelectionRectForText(cursor, status);
fragment->ComputeLocalSelectionRectForText(status); item_rect.offset += cursor.CurrentOffset();
fragment_rect.offset += fragment->InlineOffsetToContainerBox(); rect.Unite(item_rect);
rect.Unite(fragment_rect);
} }
return rect; return rect;
} }
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
namespace blink { namespace blink {
class NGBlockBreakToken; class NGBlockBreakToken;
class NGInlineCursor;
struct LayoutSelectionStatus; struct LayoutSelectionStatus;
struct NGContainerInkOverflow; struct NGContainerInkOverflow;
enum class NGOutlineType; enum class NGOutlineType;
...@@ -184,8 +185,6 @@ class CORE_EXPORT NGPaintFragment : public RefCounted<NGPaintFragment>, ...@@ -184,8 +185,6 @@ class CORE_EXPORT NGPaintFragment : public RefCounted<NGPaintFragment>,
IntRect VisualRect() const override; IntRect VisualRect() const override;
IntRect PartialInvalidationVisualRect() const override; IntRect PartialInvalidationVisualRect() const override;
PhysicalRect ComputeLocalSelectionRectForText(
const LayoutSelectionStatus&) const;
PhysicalRect ComputeLocalSelectionRectForReplaced() const; PhysicalRect ComputeLocalSelectionRectForReplaced() const;
// Set ShouldDoFullPaintInvalidation flag in the corresponding LayoutObject. // Set ShouldDoFullPaintInvalidation flag in the corresponding LayoutObject.
...@@ -429,6 +428,10 @@ extern template class CORE_EXTERN_TEMPLATE_EXPORT ...@@ -429,6 +428,10 @@ extern template class CORE_EXTERN_TEMPLATE_EXPORT
extern template class CORE_EXTERN_TEMPLATE_EXPORT extern template class CORE_EXTERN_TEMPLATE_EXPORT
NGPaintFragment::List<NGPaintFragment::TraverseNextSibling>; NGPaintFragment::List<NGPaintFragment::TraverseNextSibling>;
PhysicalRect ComputeLocalSelectionRectForText(
const NGInlineCursor& cursor,
const LayoutSelectionStatus& selection_status);
} // namespace blink } // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_NG_NG_PAINT_FRAGMENT_H_ #endif // THIRD_PARTY_BLINK_RENDERER_CORE_PAINT_NG_NG_PAINT_FRAGMENT_H_
...@@ -98,19 +98,26 @@ inline std::pair<LayoutUnit, LayoutUnit> GetLineLeftAndRightForOffsets( ...@@ -98,19 +98,26 @@ inline std::pair<LayoutUnit, LayoutUnit> GetLineLeftAndRightForOffsets(
// |NGFragmentItem| is done. http://crbug.com/982194 // |NGFragmentItem| is done. http://crbug.com/982194
inline LayoutSelectionStatus ComputeLayoutSelectionStatus( inline LayoutSelectionStatus ComputeLayoutSelectionStatus(
const NGInlineCursor& cursor) { const NGInlineCursor& cursor) {
// TODO(yosin): We should implement |NGInlineCursor| version of return cursor.CurrentItem()
// ComputeLayoutSelectionStatus ->GetLayoutObject()
return LayoutSelectionStatus(0, 0, SelectSoftLineBreak::kNotSelected); ->GetDocument()
.GetFrame()
->Selection()
.ComputeLayoutSelectionStatus(cursor);
} }
inline LayoutSelectionStatus ComputeLayoutSelectionStatus( inline LayoutSelectionStatus ComputeLayoutSelectionStatus(
const NGTextPainterCursor& cursor) { const NGTextPainterCursor& cursor) {
// Note:: Because of this function is hot, we should not use |NGInlineCursor|
// on paint fragment which requires traversing ancestors to root.
NGInlineCursor inline_cursor(cursor.RootPaintFragment());
inline_cursor.MoveTo(cursor.PaintFragment());
return cursor.CurrentItem() return cursor.CurrentItem()
->GetLayoutObject() ->GetLayoutObject()
->GetDocument() ->GetDocument()
.GetFrame() .GetFrame()
->Selection() ->Selection()
.ComputeLayoutSelectionStatus(cursor.PaintFragment()); .ComputeLayoutSelectionStatus(inline_cursor);
} }
// TODO(yosin): Remove |ComputeLocalRect| once the transition to // TODO(yosin): Remove |ComputeLocalRect| once the transition to
...@@ -305,26 +312,30 @@ void PaintDocumentMarkers(GraphicsContext& context, ...@@ -305,26 +312,30 @@ void PaintDocumentMarkers(GraphicsContext& context,
} // namespace } // namespace
const NGPaintFragment& NGTextPainterCursor::RootPaintFragment() const {
if (!root_paint_fragment_)
root_paint_fragment_ = paint_fragment_.Root();
return *root_paint_fragment_;
}
template <typename Cursor> template <typename Cursor>
NGTextFragmentPainter<Cursor>::NGTextFragmentPainter(const Cursor& cursor) NGTextFragmentPainter<Cursor>::NGTextFragmentPainter(const Cursor& cursor)
: cursor_(cursor) {} : cursor_(cursor) {}
static PhysicalRect ComputeLocalSelectionRectForText(
const NGTextPainterCursor& cursor,
const LayoutSelectionStatus& selection_status) {
NGInlineCursor inline_cursor(cursor.RootPaintFragment());
inline_cursor.MoveTo(cursor.PaintFragment());
return ComputeLocalSelectionRectForText(inline_cursor, selection_status);
}
// Logic is copied from InlineTextBoxPainter::PaintSelection. // Logic is copied from InlineTextBoxPainter::PaintSelection.
// |selection_start| and |selection_end| should be between // |selection_start| and |selection_end| should be between
// [text_fragment.StartOffset(), text_fragment.EndOffset()]. // [text_fragment.StartOffset(), text_fragment.EndOffset()].
// TODO(yosin): We should implement |NGInlineCursor| version of template <typename Cursor>
// |PaintSelection()|
static void PaintSelection(GraphicsContext& context,
const NGInlineCursor& cursor,
Node* node,
const Document& document,
const ComputedStyle& style,
Color text_color,
const PhysicalRect& box_rect,
const LayoutSelectionStatus& selection_status) {}
static void PaintSelection(GraphicsContext& context, static void PaintSelection(GraphicsContext& context,
const NGTextPainterCursor& cursor, const Cursor& cursor,
Node* node, Node* node,
const Document& document, const Document& document,
const ComputedStyle& style, const ComputedStyle& style,
...@@ -334,7 +345,7 @@ static void PaintSelection(GraphicsContext& context, ...@@ -334,7 +345,7 @@ static void PaintSelection(GraphicsContext& context,
const Color color = const Color color =
SelectionBackgroundColor(document, style, node, text_color); SelectionBackgroundColor(document, style, node, text_color);
const PhysicalRect selection_rect = const PhysicalRect selection_rect =
cursor.PaintFragment().ComputeLocalSelectionRectForText(selection_status); ComputeLocalSelectionRectForText(cursor, selection_status);
PaintRect(context, box_rect.offset, selection_rect, color); PaintRect(context, box_rect.offset, selection_rect, color);
} }
......
...@@ -38,10 +38,12 @@ class NGTextPainterCursor { ...@@ -38,10 +38,12 @@ class NGTextPainterCursor {
const NGPaintFragment& PaintFragment() const { return paint_fragment_; } const NGPaintFragment& PaintFragment() const { return paint_fragment_; }
const NGPhysicalTextFragment* CurrentItem() const { return &text_fragment_; } const NGPhysicalTextFragment* CurrentItem() const { return &text_fragment_; }
const NGPaintFragment& RootPaintFragment() const;
private: private:
const NGPaintFragment& paint_fragment_; const NGPaintFragment& paint_fragment_;
const NGPhysicalTextFragment& text_fragment_; const NGPhysicalTextFragment& text_fragment_;
mutable const NGPaintFragment* root_paint_fragment_ = nullptr;
}; };
// Text fragment painter for LayoutNG. Operates on NGPhysicalTextFragments and // Text fragment painter for LayoutNG. Operates on NGPhysicalTextFragments and
......
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