Commit e918f504 authored by Xiaocheng Hu's avatar Xiaocheng Hu Committed by Commit Bot

[LayoutNG] Implement LayoutReplaced::PositionForPoint

The legacy implementation of LayoutReplaced::PositionForPoint()
almost works in LayoutNG, except for the case where the replaced
object is in inline layout, where it fails to obtain the line's
logical top and bottom in the containing block.

This patch adds LayoutNG support for it.

Cq-Include-Trybots: master.tryserver.blink:linux_trusty_blink_rel;master.tryserver.chromium.linux:linux_layout_tests_layout_ng;master.tryserver.chromium.linux:linux_layout_tests_slimming_paint_v2
Change-Id: I414a445e0617770aec6461dbc497f650f586d12b
Reviewed-on: https://chromium-review.googlesource.com/949864
Commit-Queue: Xiaocheng Hu <xiaochengh@chromium.org>
Reviewed-by: default avatarYoshifumi Inoue <yosin@chromium.org>
Reviewed-by: default avatarEmil A Eklund <eae@chromium.org>
Cr-Commit-Position: refs/heads/master@{#541966}
parent 48e8640c
<!DOCTYPE html>
<script src="../../../../resources/testharness.js"></script>
<script src="../../../../resources/testharnessreport.js"></script>
<div id='horizontal-container'>
<img id='img-h1' style='width: 100px; height: 50px'><img id='img-h2' style='width: 100px; height: 100px'>
</div>
<div id='vertical-container' style='writing-mode: vertical-lr'>
<img id='img-v1' style='width: 50px; height: 100px'><img id='img-v2' style='width: 100px; height: 100px'>
</div>
<script>
// This file tests hit-testing outside image while inside containing line box.
test(() => {
// Hit test location indicated by '*':
// * 2222
// 2222
// 11112222
// 11112222
const container = document.getElementById('horizontal-container');
const target = document.getElementById('img-h1');
const x = container.offsetLeft + 25;
const y = container.offsetTop + 25;
const result = document.caretRangeFromPoint(x, y);
assert_true(result instanceof Range);
assert_equals(result.startContainer, container);
assert_equals(container.childNodes[result.startOffset], target);
assert_true(result.collapsed);
}, 'Hit test before half-width of image in horizontal writing mode');
test(() => {
// Hit test location indicated by '*':
// *2222
// 2222
// 11112222
// 11112222
const container = document.getElementById('horizontal-container');
const target = document.getElementById('img-h2');
const x = container.offsetLeft + 75;
const y = container.offsetTop + 25;
const result = document.caretRangeFromPoint(x, y);
assert_true(result instanceof Range);
assert_equals(result.startContainer, container);
assert_equals(container.childNodes[result.startOffset], target);
assert_true(result.collapsed);
}, 'Hit test after half-width of image in horizontal writing mode');
test(() => {
// Hit test location indicated by '*':
// * 1111
// 1111
// 22222222
// 22222222
const container = document.getElementById('vertical-container');
const target = document.getElementById('img-v1');
const x = container.offsetLeft + 10;
const y = container.offsetTop + 25;
const result = document.caretRangeFromPoint(x, y);
assert_true(result instanceof Range);
assert_equals(result.startContainer, container);
assert_equals(container.childNodes[result.startOffset], target);
assert_true(result.collapsed);
}, 'Hit test before half-width of image in vertical writing mode');
test(() => {
// Hit test location indicated by '*':
// 1111
// * 1111
// 22222222
// 22222222
const container = document.getElementById('vertical-container');
const target = document.getElementById('img-v2');
const x = container.offsetLeft + 10;
const y = container.offsetTop + 75;
const result = document.caretRangeFromPoint(x, y);
assert_true(result instanceof Range);
assert_equals(result.startContainer, container);
assert_equals(container.childNodes[result.startOffset], target);
assert_true(result.collapsed);
}, 'Hit test after half-width of image in vertical writing mode');
</script>
...@@ -30,9 +30,13 @@ ...@@ -30,9 +30,13 @@
#include "core/layout/LayoutInline.h" #include "core/layout/LayoutInline.h"
#include "core/layout/LayoutVideo.h" #include "core/layout/LayoutVideo.h"
#include "core/layout/api/LineLayoutBlockFlow.h" #include "core/layout/api/LineLayoutBlockFlow.h"
#include "core/layout/ng/geometry/ng_logical_offset.h"
#include "core/layout/ng/geometry/ng_logical_size.h"
#include "core/layout/ng/ng_physical_box_fragment.h"
#include "core/paint/PaintInfo.h" #include "core/paint/PaintInfo.h"
#include "core/paint/PaintLayer.h" #include "core/paint/PaintLayer.h"
#include "core/paint/ReplacedPainter.h" #include "core/paint/ReplacedPainter.h"
#include "core/paint/ng/ng_paint_fragment.h"
#include "platform/LengthFunctions.h" #include "platform/LengthFunctions.h"
namespace blink { namespace blink {
...@@ -875,14 +879,62 @@ void LayoutReplaced::ComputePreferredLogicalWidths() { ...@@ -875,14 +879,62 @@ void LayoutReplaced::ComputePreferredLogicalWidths() {
ClearPreferredLogicalWidthsDirty(); ClearPreferredLogicalWidthsDirty();
} }
PositionWithAffinity LayoutReplaced::PositionForPoint( static std::pair<LayoutUnit, LayoutUnit> SelectionTopAndBottom(
const LayoutPoint& point) const { const LayoutReplaced& layout_replaced) {
// FIXME: This code is buggy if the replaced element is relative positioned. // TODO(layout-dev): This code is buggy if the replaced element is relative
InlineBox* box = InlineBoxWrapper(); // positioned.
// The fallback answer when we can't find the containing line box of
// |layout_replaced|.
const std::pair<LayoutUnit, LayoutUnit> fallback(
layout_replaced.LogicalTop(), layout_replaced.LogicalBottom());
const NGPaintFragment* inline_container =
layout_replaced.IsInline()
? NGPaintFragment::GetForInlineContainer(&layout_replaced)
: nullptr;
if (inline_container) {
// Step 1: Find the line box containing |layout_replaced|.
const NGPaintFragment* inline_fragment =
*NGPaintFragment::InlineFragmentsFor(&layout_replaced).begin();
if (!inline_fragment)
return fallback;
const NGPaintFragment* line_box_container =
inline_fragment->ContainerLineBox();
if (!line_box_container)
return fallback;
// Step 2: Return the logical top and bottom of the line box.
// TODO(layout-dev): Use selection top & bottom instead of line's, or decide
// if we still want to distinguish line and selection heights in NG.
const ComputedStyle& line_style = line_box_container->Style();
const WritingMode writing_mode = line_style.GetWritingMode();
const TextDirection text_direction = line_style.Direction();
const NGPhysicalOffset line_box_offset =
line_box_container->InlineOffsetToContainerBox();
const NGPhysicalSize line_box_size = line_box_container->Size();
const NGLogicalOffset logical_offset = line_box_offset.ConvertToLogical(
writing_mode, text_direction, inline_container->Size(),
line_box_container->Size());
const NGLogicalSize logical_size =
line_box_size.ConvertToLogical(writing_mode);
return {logical_offset.block_offset,
logical_offset.block_offset + logical_size.block_size};
}
InlineBox* box = layout_replaced.InlineBoxWrapper();
RootInlineBox* root_box = box ? &box->Root() : nullptr; RootInlineBox* root_box = box ? &box->Root() : nullptr;
if (!root_box)
return fallback;
LayoutUnit top = root_box ? root_box->SelectionTop() : LogicalTop(); return {root_box->SelectionTop(), root_box->SelectionBottom()};
LayoutUnit bottom = root_box ? root_box->SelectionBottom() : LogicalBottom(); }
PositionWithAffinity LayoutReplaced::PositionForPoint(
const LayoutPoint& point) const {
LayoutUnit top;
LayoutUnit bottom;
std::tie(top, bottom) = SelectionTopAndBottom(*this);
LayoutUnit block_direction_position = IsHorizontalWritingMode() LayoutUnit block_direction_position = IsHorizontalWritingMode()
? point.Y() + Location().Y() ? point.Y() + Location().Y()
......
...@@ -193,4 +193,15 @@ LayoutRect NGPaintFragment::PartialInvalidationRect() const { ...@@ -193,4 +193,15 @@ LayoutRect NGPaintFragment::PartialInvalidationRect() const {
return block_fragment->VisualRect(); return block_fragment->VisualRect();
} }
const NGPaintFragment* NGPaintFragment::ContainerLineBox() const {
DCHECK(PhysicalFragment().IsInline());
for (const NGPaintFragment* runner = this; runner;
runner = runner->Parent()) {
if (runner->PhysicalFragment().IsLineBox())
return runner;
}
NOTREACHED();
return nullptr;
}
} // namespace blink } // namespace blink
...@@ -50,9 +50,12 @@ class CORE_EXPORT NGPaintFragment : public DisplayItemClient, ...@@ -50,9 +50,12 @@ class CORE_EXPORT NGPaintFragment : public DisplayItemClient,
return children_; return children_;
} }
// Returns offset to its container box for inline fragments. // Returns the container line box for inline fragments.
const NGPaintFragment* ContainerLineBox() const;
// Returns offset to its container box for inline and line box fragments.
const NGPhysicalOffset& InlineOffsetToContainerBox() const { const NGPhysicalOffset& InlineOffsetToContainerBox() const {
DCHECK(PhysicalFragment().IsInline()); DCHECK(PhysicalFragment().IsInline() || PhysicalFragment().IsLineBox());
return inline_offset_to_container_box_; return inline_offset_to_container_box_;
} }
......
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