Commit b2551a09 authored by Chris Harrelson's avatar Chris Harrelson Committed by Commit Bot

[IOv2] Support rect-based hit testing for SVG and content under foreignObject.

This is needed to support IOv2 occlusion testing via hit test. It also allows
content under foreignObject to receive disambiguation on touch screens.

Bug: 823796

Cq-Include-Trybots: luci.chromium.try:android_optional_gpu_tests_rel;luci.chromium.try:linux_layout_tests_slimming_paint_v2;master.tryserver.blink:linux_trusty_blink_rel
Change-Id: I80089feb9a30d3f28ce2c68f7276cafc5410adcb
Reviewed-on: https://chromium-review.googlesource.com/1227470
Commit-Queue: Chris Harrelson <chrishtr@chromium.org>
Reviewed-by: default avatarFredrik Söderquist <fs@opera.com>
Cr-Commit-Position: refs/heads/master@{#595104}
parent f4097091
...@@ -2043,6 +2043,7 @@ jumbo_source_set("unit_tests") { ...@@ -2043,6 +2043,7 @@ jumbo_source_set("unit_tests") {
"layout/shapes/box_shape_test.cc", "layout/shapes/box_shape_test.cc",
"layout/svg/layout_svg_foreign_object_test.cc", "layout/svg/layout_svg_foreign_object_test.cc",
"layout/svg/layout_svg_root_test.cc", "layout/svg/layout_svg_root_test.cc",
"layout/svg/layout_svg_text_test.cc",
"layout/text_autosizer_test.cc", "layout/text_autosizer_test.cc",
"layout/visual_rect_mapping_test.cc", "layout/visual_rect_mapping_test.cc",
"loader/allowed_by_nosniff_test.cc", "loader/allowed_by_nosniff_test.cc",
......
...@@ -121,13 +121,23 @@ bool HitTestLocation::Intersects(const LayoutRect& rect) const { ...@@ -121,13 +121,23 @@ bool HitTestLocation::Intersects(const LayoutRect& rect) const {
} }
bool HitTestLocation::Intersects(const FloatRect& rect) const { bool HitTestLocation::Intersects(const FloatRect& rect) const {
return IntersectsRect(rect, FloatRect(bounding_box_)); if (is_rect_based_)
return transformed_rect_.IntersectsRect(rect);
return rect.Contains(transformed_point_);
} }
bool HitTestLocation::Intersects(const FloatRoundedRect& rect) const { bool HitTestLocation::Intersects(const FloatRoundedRect& rect) const {
return rect.IntersectsQuad(transformed_rect_); return rect.IntersectsQuad(transformed_rect_);
} }
bool HitTestLocation::Intersects(const FloatQuad& quad) const {
// TODO(chrishtr): if the quads are not rectilinear, calling Intersects
// has false positives.
if (is_rect_based_)
return Intersects(quad.BoundingBox());
return quad.ContainsPoint(FloatPoint(point_));
}
bool HitTestLocation::ContainsPoint(const FloatPoint& point) const { bool HitTestLocation::ContainsPoint(const FloatPoint& point) const {
return transformed_rect_.ContainsPoint(point); return transformed_rect_.ContainsPoint(point);
} }
......
...@@ -70,8 +70,11 @@ class CORE_EXPORT HitTestLocation { ...@@ -70,8 +70,11 @@ class CORE_EXPORT HitTestLocation {
} }
bool Intersects(const LayoutRect&) const; bool Intersects(const LayoutRect&) const;
// Uses floating-point intersection, which uses inclusive intersection
// (see LayoutRect::InclusiveIntersect for a definition)
bool Intersects(const FloatRect&) const; bool Intersects(const FloatRect&) const;
bool Intersects(const FloatRoundedRect&) const; bool Intersects(const FloatRoundedRect&) const;
bool Intersects(const FloatQuad&) const;
bool ContainsPoint(const FloatPoint&) const; bool ContainsPoint(const FloatPoint&) const;
const FloatPoint& TransformedPoint() const { return transformed_point_; } const FloatPoint& TransformedPoint() const { return transformed_point_; }
......
...@@ -3818,13 +3818,6 @@ AffineTransform LayoutObject::LocalSVGTransform() const { ...@@ -3818,13 +3818,6 @@ AffineTransform LayoutObject::LocalSVGTransform() const {
return AffineTransform(); return AffineTransform();
} }
bool LayoutObject::NodeAtFloatPoint(HitTestResult&,
const FloatPoint&,
HitTestAction) {
NOTREACHED();
return false;
}
bool LayoutObject::IsRelayoutBoundaryForInspector() const { bool LayoutObject::IsRelayoutBoundaryForInspector() const {
return ObjectIsRelayoutBoundary(this); return ObjectIsRelayoutBoundary(this);
} }
......
...@@ -794,13 +794,6 @@ class CORE_EXPORT LayoutObject : public ImageResourceObserver, ...@@ -794,13 +794,6 @@ class CORE_EXPORT LayoutObject : public ImageResourceObserver,
return LocalSVGTransform(); return LocalSVGTransform();
} }
// SVG uses FloatPoint precise hit testing, and passes the point in parent
// coordinates instead of in paint invalidation container coordinates.
// Eventually the rest of the layout tree will move to a similar model.
virtual bool NodeAtFloatPoint(HitTestResult&,
const FloatPoint& point_in_parent,
HitTestAction);
// End of SVG-specific methods. // End of SVG-specific methods.
bool IsAnonymous() const { return bitfields_.IsAnonymous(); } bool IsAnonymous() const { return bitfields_.IsAnonymous(); }
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#include "third_party/blink/renderer/core/layout/hit_test_result.h" #include "third_party/blink/renderer/core/layout/hit_test_result.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/svg/layout_svg_foreign_object.h"
#include "third_party/blink/renderer/core/layout/svg/svg_layout_support.h" #include "third_party/blink/renderer/core/layout/svg/svg_layout_support.h"
#include "third_party/blink/renderer/core/layout/svg/svg_resources.h" #include "third_party/blink/renderer/core/layout/svg/svg_resources.h"
#include "third_party/blink/renderer/core/layout/svg/svg_resources_cache.h" #include "third_party/blink/renderer/core/layout/svg/svg_resources_cache.h"
...@@ -173,37 +174,49 @@ void LayoutSVGContainer::UpdateCachedBoundaries() { ...@@ -173,37 +174,49 @@ void LayoutSVGContainer::UpdateCachedBoundaries() {
GetElement()->SetNeedsResizeObserverUpdate(); GetElement()->SetNeedsResizeObserverUpdate();
} }
bool LayoutSVGContainer::NodeAtFloatPoint(HitTestResult& result, bool LayoutSVGContainer::NodeAtPoint(
const FloatPoint& point_in_parent, HitTestResult& result,
HitTestAction hit_test_action) { const HitTestLocation& location_in_container,
FloatPoint local_point; const LayoutPoint& accumulated_offset,
HitTestAction hit_test_action) {
DCHECK_EQ(accumulated_offset, LayoutPoint());
HitTestLocation local_location;
if (!SVGLayoutSupport::TransformToUserSpaceAndCheckClipping( if (!SVGLayoutSupport::TransformToUserSpaceAndCheckClipping(
*this, LocalToSVGParentTransform(), point_in_parent, local_point)) *this, LocalToSVGParentTransform(), location_in_container,
local_location))
return false; return false;
for (LayoutObject* child = LastChild(); child; for (LayoutObject* child = LastChild(); child;
child = child->PreviousSibling()) { child = child->PreviousSibling()) {
if (child->NodeAtFloatPoint(result, local_point, hit_test_action)) { bool found = false;
const LayoutPoint& local_layout_point = LayoutPoint(local_point); if (child->IsSVGForeignObject()) {
found = ToLayoutSVGForeignObject(child)->NodeAtPointFromSVG(
result, local_location, accumulated_offset, hit_test_action);
} else {
found = child->NodeAtPoint(result, local_location, accumulated_offset,
hit_test_action);
}
if (found) {
const LayoutPoint& local_layout_point =
LayoutPoint(local_location.TransformedPoint());
UpdateHitTestResult(result, local_layout_point); UpdateHitTestResult(result, local_layout_point);
HitTestLocation location(local_layout_point); if (result.AddNodeToListBasedTestResult(
if (result.AddNodeToListBasedTestResult(child->GetNode(), location) == child->GetNode(), local_location) == kStopHitTesting) {
kStopHitTesting)
return true; return true;
}
} }
} }
// pointer-events: bounding-box makes it possible for containers to be direct // pointer-events: bounding-box makes it possible for containers to be direct
// targets. // targets.
if (StyleRef().PointerEvents() == EPointerEvents::kBoundingBox) { if (StyleRef().PointerEvents() == EPointerEvents::kBoundingBox) {
// Check for a valid bounding box because it will be invalid for empty // Check for a valid bounding box because it will be invalid for empty
// containers. // containers.
if (IsObjectBoundingBoxValid() && if (IsObjectBoundingBoxValid() &&
ObjectBoundingBox().Contains(local_point)) { local_location.Intersects(ObjectBoundingBox())) {
const LayoutPoint& local_layout_point = LayoutPoint(local_point); const LayoutPoint& local_layout_point =
LayoutPoint(local_location.TransformedPoint());
UpdateHitTestResult(result, local_layout_point); UpdateHitTestResult(result, local_layout_point);
HitTestLocation location(local_layout_point); if (result.AddNodeToListBasedTestResult(GetElement(), local_location) ==
if (result.AddNodeToListBasedTestResult(GetElement(), location) ==
kStopHitTesting) kStopHitTesting)
return true; return true;
} }
......
...@@ -82,9 +82,10 @@ class LayoutSVGContainer : public LayoutSVGModelObject { ...@@ -82,9 +82,10 @@ class LayoutSVGContainer : public LayoutSVGModelObject {
FloatRect StrokeBoundingBox() const final { return stroke_bounding_box_; } FloatRect StrokeBoundingBox() const final { return stroke_bounding_box_; }
bool NodeAtFloatPoint(HitTestResult&, bool NodeAtPoint(HitTestResult&,
const FloatPoint& point_in_parent, const HitTestLocation& location_in_container,
HitTestAction) override; const LayoutPoint& accumulated_offset,
HitTestAction) override;
// Called during layout to update the local transform. // Called during layout to update the local transform.
virtual SVGTransformChange CalculateLocalTransform(); virtual SVGTransformChange CalculateLocalTransform();
......
...@@ -124,21 +124,33 @@ void LayoutSVGForeignObject::UpdateLayout() { ...@@ -124,21 +124,33 @@ void LayoutSVGForeignObject::UpdateLayout() {
SVGResourcesCache::ClientLayoutChanged(*this); SVGResourcesCache::ClientLayoutChanged(*this);
} }
bool LayoutSVGForeignObject::NodeAtFloatPoint(HitTestResult& result, bool LayoutSVGForeignObject::NodeAtPointFromSVG(
const FloatPoint& point_in_parent, HitTestResult& result,
HitTestAction hit_test_action) { const HitTestLocation& location_in_parent,
const LayoutPoint& accumulated_offset,
HitTestAction) {
DCHECK_EQ(accumulated_offset, LayoutPoint());
AffineTransform local_transform = LocalSVGTransform(); AffineTransform local_transform = LocalSVGTransform();
if (!local_transform.IsInvertible()) if (!local_transform.IsInvertible())
return false; return false;
FloatPoint local_point = local_transform.Inverse().MapPoint(point_in_parent); AffineTransform inverse = local_transform.Inverse();
LayoutPoint point_in_foreign_object(local_point); base::Optional<HitTestLocation> local_location;
if (location_in_parent.IsRectBasedTest()) {
local_location.emplace(
inverse.MapPoint(location_in_parent.TransformedPoint()),
inverse.MapQuad(location_in_parent.TransformedRect()));
} else {
local_location.emplace(
(inverse.MapPoint(location_in_parent.TransformedPoint())));
}
// |local_point| already includes the offset of the <foreignObject> element, // |local_point| already includes the offset of the <foreignObject> element,
// but PaintLayer::HitTestLayer assumes it has not been. // but PaintLayer::HitTestLayer assumes it has not been.
point_in_foreign_object.MoveBy(-Layer()->LayoutBoxLocation()); HitTestLocation local_without_offset(
HitTestLocation location(point_in_foreign_object); *local_location, -ToLayoutSize(Layer()->LayoutBoxLocation()));
HitTestResult layer_result(result.GetHitTestRequest(), location); HitTestResult layer_result(result.GetHitTestRequest(), local_without_offset);
bool retval = Layer()->HitTest(location, layer_result, bool retval = Layer()->HitTest(local_without_offset, layer_result,
LayoutRect(LayoutRect::InfiniteIntRect())); LayoutRect(LayoutRect::InfiniteIntRect()));
// Preserve the "point in inner node frame" from the original request, // Preserve the "point in inner node frame" from the original request,
......
...@@ -65,9 +65,15 @@ class LayoutSVGForeignObject final : public LayoutSVGBlock { ...@@ -65,9 +65,15 @@ class LayoutSVGForeignObject final : public LayoutSVGBlock {
const LayoutPoint&, const LayoutPoint&,
HitTestAction) override; HitTestAction) override;
bool NodeAtFloatPoint(HitTestResult&, // A method to call when recursively hit testing from an SVG parent.
const FloatPoint& point_in_parent, // Since LayoutSVGRoot has a PaintLayer always, this will cause a
HitTestAction) override; // trampoline through PaintLayer::HitTest and back to a call to NodeAtPoint
// on this object. This is why there are two methods.
bool NodeAtPointFromSVG(HitTestResult&,
const HitTestLocation&,
const LayoutPoint&,
HitTestAction);
bool IsOfType(LayoutObjectType type) const override { bool IsOfType(LayoutObjectType type) const override {
return type == kLayoutObjectSVGForeignObject || return type == kLayoutObjectSVGForeignObject ||
LayoutSVGBlock::IsOfType(type); LayoutSVGBlock::IsOfType(type);
......
...@@ -68,6 +68,17 @@ TEST_F(LayoutSVGForeignObjectTest, DivInForeignObject) { ...@@ -68,6 +68,17 @@ TEST_F(LayoutSVGForeignObjectTest, DivInForeignObject) {
EXPECT_EQ(div.GetNode(), HitTest(349, 249)); EXPECT_EQ(div.GetNode(), HitTest(349, 249));
EXPECT_EQ(foreign, HitTest(350, 250)); EXPECT_EQ(foreign, HitTest(350, 250));
EXPECT_EQ(svg, HitTest(450, 350)); EXPECT_EQ(svg, HitTest(450, 350));
// Rect based hit testing
auto results = RectBasedHitTest(LayoutRect(0, 0, 300, 300));
int count = 0;
EXPECT_EQ(3u, results.size());
for (auto result : results) {
Node* node = result.Get();
if (node == svg || node == div.GetNode() || node == foreign)
count++;
}
EXPECT_EQ(3, count);
} }
TEST_F(LayoutSVGForeignObjectTest, IframeInForeignObject) { TEST_F(LayoutSVGForeignObjectTest, IframeInForeignObject) {
...@@ -75,7 +86,7 @@ TEST_F(LayoutSVGForeignObjectTest, IframeInForeignObject) { ...@@ -75,7 +86,7 @@ TEST_F(LayoutSVGForeignObjectTest, IframeInForeignObject) {
<style>body { margin: 0 }</style> <style>body { margin: 0 }</style>
<svg id='svg' style='width: 500px; height: 450px'> <svg id='svg' style='width: 500px; height: 450px'>
<foreignObject id='foreign' x='100' y='100' width='300' height='250'> <foreignObject id='foreign' x='100' y='100' width='300' height='250'>
<iframe style='border: none; margin: 30px; <iframe id=iframe style='border: none; margin: 30px;
width: 240px; height: 190px'></iframe> width: 240px; height: 190px'></iframe>
</foreignObject> </foreignObject>
</svg> </svg>
...@@ -92,6 +103,7 @@ TEST_F(LayoutSVGForeignObjectTest, IframeInForeignObject) { ...@@ -92,6 +103,7 @@ TEST_F(LayoutSVGForeignObjectTest, IframeInForeignObject) {
const auto& svg = *GetDocument().getElementById("svg"); const auto& svg = *GetDocument().getElementById("svg");
const auto& foreign = *GetDocument().getElementById("foreign"); const auto& foreign = *GetDocument().getElementById("foreign");
const auto& foreign_object = *GetLayoutObjectByElementId("foreign"); const auto& foreign_object = *GetLayoutObjectByElementId("foreign");
const auto& iframe = *GetDocument().getElementById("iframe");
const auto& div = *ChildDocument().getElementById("div")->GetLayoutObject(); const auto& div = *ChildDocument().getElementById("div")->GetLayoutObject();
EXPECT_EQ(FloatRect(100, 100, 300, 250), foreign_object.ObjectBoundingBox()); EXPECT_EQ(FloatRect(100, 100, 300, 250), foreign_object.ObjectBoundingBox());
...@@ -136,6 +148,18 @@ TEST_F(LayoutSVGForeignObjectTest, IframeInForeignObject) { ...@@ -136,6 +148,18 @@ TEST_F(LayoutSVGForeignObjectTest, IframeInForeignObject) {
EXPECT_EQ(ChildDocument().documentElement(), HitTest(369, 319)); EXPECT_EQ(ChildDocument().documentElement(), HitTest(369, 319));
EXPECT_EQ(foreign, HitTest(370, 320)); EXPECT_EQ(foreign, HitTest(370, 320));
EXPECT_EQ(svg, HitTest(450, 400)); EXPECT_EQ(svg, HitTest(450, 400));
// Rect based hit testing
auto results = RectBasedHitTest(LayoutRect(0, 0, 300, 300));
int count = 0;
EXPECT_EQ(7u, results.size());
for (auto result : results) {
Node* node = result.Get();
if (node == svg || node == div.GetNode() || node == foreign ||
node == iframe)
count++;
}
EXPECT_EQ(4, count);
} }
TEST_F(LayoutSVGForeignObjectTest, HitTestZoomedForeignObject) { TEST_F(LayoutSVGForeignObjectTest, HitTestZoomedForeignObject) {
...@@ -184,6 +208,17 @@ TEST_F(LayoutSVGForeignObjectTest, HitTestZoomedForeignObject) { ...@@ -184,6 +208,17 @@ TEST_F(LayoutSVGForeignObjectTest, HitTestZoomedForeignObject) {
EXPECT_EQ(svg, HitTest(20, 20)); EXPECT_EQ(svg, HitTest(20, 20));
EXPECT_EQ(foreign, HitTest(280, 280)); EXPECT_EQ(foreign, HitTest(280, 280));
EXPECT_EQ(div, HitTest(290, 290)); EXPECT_EQ(div, HitTest(290, 290));
// Rect based hit testing
auto results = RectBasedHitTest(LayoutRect(0, 0, 300, 300));
int count = 0;
EXPECT_EQ(3u, results.size());
for (auto result : results) {
Node* node = result.Get();
if (node == svg || node == &div || node == foreign)
count++;
}
EXPECT_EQ(3, count);
} }
TEST_F(LayoutSVGForeignObjectTest, HitTestViewBoxForeignObject) { TEST_F(LayoutSVGForeignObjectTest, HitTestViewBoxForeignObject) {
......
...@@ -43,9 +43,10 @@ void LayoutSVGHiddenContainer::UpdateLayout() { ...@@ -43,9 +43,10 @@ void LayoutSVGHiddenContainer::UpdateLayout() {
ClearNeedsLayout(); ClearNeedsLayout();
} }
bool LayoutSVGHiddenContainer::NodeAtFloatPoint(HitTestResult&, bool LayoutSVGHiddenContainer::NodeAtPoint(HitTestResult&,
const FloatPoint&, const HitTestLocation&,
HitTestAction) { const LayoutPoint&,
HitTestAction) {
return false; return false;
} }
......
...@@ -55,9 +55,10 @@ class LayoutSVGHiddenContainer : public LayoutSVGContainer { ...@@ -55,9 +55,10 @@ class LayoutSVGHiddenContainer : public LayoutSVGContainer {
void AbsoluteQuads(Vector<FloatQuad>&, void AbsoluteQuads(Vector<FloatQuad>&,
MapCoordinatesFlags mode = 0) const final {} MapCoordinatesFlags mode = 0) const final {}
bool NodeAtFloatPoint(HitTestResult&, bool NodeAtPoint(HitTestResult&,
const FloatPoint& point_in_parent, const HitTestLocation& location_in_container,
HitTestAction) final; const LayoutPoint& accumulated_offset,
HitTestAction) final;
}; };
} // namespace blink } // namespace blink
......
...@@ -166,9 +166,11 @@ void LayoutSVGImage::Paint(const PaintInfo& paint_info) const { ...@@ -166,9 +166,11 @@ void LayoutSVGImage::Paint(const PaintInfo& paint_info) const {
SVGImagePainter(*this).Paint(paint_info); SVGImagePainter(*this).Paint(paint_info);
} }
bool LayoutSVGImage::NodeAtFloatPoint(HitTestResult& result, bool LayoutSVGImage::NodeAtPoint(HitTestResult& result,
const FloatPoint& point_in_parent, const HitTestLocation& location_in_container,
HitTestAction hit_test_action) { const LayoutPoint& accumulated_offset,
HitTestAction hit_test_action) {
DCHECK(accumulated_offset == LayoutPoint());
// We only draw in the forground phase, so we only hit-test then. // We only draw in the forground phase, so we only hit-test then.
if (hit_test_action != kHitTestForeground) if (hit_test_action != kHitTestForeground)
return false; return false;
...@@ -180,17 +182,18 @@ bool LayoutSVGImage::NodeAtFloatPoint(HitTestResult& result, ...@@ -180,17 +182,18 @@ bool LayoutSVGImage::NodeAtFloatPoint(HitTestResult& result,
if (hit_rules.require_visible && style.Visibility() != EVisibility::kVisible) if (hit_rules.require_visible && style.Visibility() != EVisibility::kVisible)
return false; return false;
FloatPoint local_point; HitTestLocation local_location;
if (!SVGLayoutSupport::TransformToUserSpaceAndCheckClipping( if (!SVGLayoutSupport::TransformToUserSpaceAndCheckClipping(
*this, LocalToSVGParentTransform(), point_in_parent, local_point)) *this, LocalToSVGParentTransform(), location_in_container,
local_location))
return false; return false;
if (hit_rules.can_hit_fill || hit_rules.can_hit_bounding_box) { if (hit_rules.can_hit_fill || hit_rules.can_hit_bounding_box) {
if (object_bounding_box_.Contains(local_point)) { if (local_location.Intersects(object_bounding_box_)) {
const LayoutPoint& local_layout_point = LayoutPoint(local_point); const LayoutPoint& local_layout_point =
HitTestLocation location(local_layout_point); LayoutPoint(local_location.TransformedPoint());
UpdateHitTestResult(result, local_layout_point); UpdateHitTestResult(result, local_layout_point);
if (result.AddNodeToListBasedTestResult(GetElement(), location) == if (result.AddNodeToListBasedTestResult(GetElement(), local_location) ==
kStopHitTesting) kStopHitTesting)
return true; return true;
} }
......
...@@ -67,9 +67,10 @@ class LayoutSVGImage final : public LayoutSVGModelObject { ...@@ -67,9 +67,10 @@ class LayoutSVGImage final : public LayoutSVGModelObject {
bool UpdateBoundingBox(); bool UpdateBoundingBox();
bool NodeAtFloatPoint(HitTestResult&, bool NodeAtPoint(HitTestResult&,
const FloatPoint& point_in_parent, const HitTestLocation& location_in_parent,
HitTestAction) override; const LayoutPoint& accumulated_offset,
HitTestAction) override;
AffineTransform LocalSVGTransform() const override { AffineTransform LocalSVGTransform() const override {
return local_transform_; return local_transform_;
......
...@@ -158,12 +158,4 @@ void LayoutSVGModelObject::StyleDidChange(StyleDifference diff, ...@@ -158,12 +158,4 @@ void LayoutSVGModelObject::StyleDidChange(StyleDifference diff,
SVGResourcesCache::ClientStyleChanged(*this, diff, StyleRef()); SVGResourcesCache::ClientStyleChanged(*this, diff, StyleRef());
} }
bool LayoutSVGModelObject::NodeAtPoint(HitTestResult&,
const HitTestLocation&,
const LayoutPoint&,
HitTestAction) {
NOTREACHED();
return false;
}
} // namespace blink } // namespace blink
...@@ -95,11 +95,6 @@ class LayoutSVGModelObject : public LayoutObject { ...@@ -95,11 +95,6 @@ class LayoutSVGModelObject : public LayoutObject {
// LayoutSVGModelObject subclasses should use GetElement() instead. // LayoutSVGModelObject subclasses should use GetElement() instead.
void GetNode() const = delete; void GetNode() const = delete;
// This method should never be called, SVG uses a different nodeAtPoint method
bool NodeAtPoint(HitTestResult&,
const HitTestLocation& location_in_container,
const LayoutPoint& accumulated_offset,
HitTestAction) final;
void AddOutlineRects(Vector<LayoutRect>&, void AddOutlineRects(Vector<LayoutRect>&,
const LayoutPoint& additional_offset, const LayoutPoint& additional_offset,
NGOutlineType) const final; NGOutlineType) const final;
......
...@@ -252,14 +252,15 @@ bool LayoutSVGResourceClipper::HitTestClipContent( ...@@ -252,14 +252,15 @@ bool LayoutSVGResourceClipper::HitTestClipContent(
Traversal<SVGElement>::ChildrenOf(*GetElement())) { Traversal<SVGElement>::ChildrenOf(*GetElement())) {
if (!ContributesToClip(child_element)) if (!ContributesToClip(child_element))
continue; continue;
HitTestLocation location((LayoutPoint())); HitTestLocation location(point);
HitTestResult result(HitTestRequest::kSVGClipContent, location); HitTestResult result(HitTestRequest::kSVGClipContent, location);
LayoutObject* layout_object = child_element.GetLayoutObject(); LayoutObject* layout_object = child_element.GetLayoutObject();
DCHECK(!layout_object->IsBoxModelObject() || DCHECK(!layout_object->IsBoxModelObject() ||
!ToLayoutBoxModelObject(layout_object)->HasSelfPaintingLayer()); !ToLayoutBoxModelObject(layout_object)->HasSelfPaintingLayer());
if (layout_object->NodeAtFloatPoint(result, point, kHitTestForeground)) if (layout_object->NodeAtPoint(result, location, LayoutPoint(),
kHitTestForeground))
return true; return true;
} }
return false; return false;
......
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
#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_embedded_content.h" #include "third_party/blink/renderer/core/layout/layout_embedded_content.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/svg/layout_svg_foreign_object.h"
#include "third_party/blink/renderer/core/layout/svg/layout_svg_resource_masker.h" #include "third_party/blink/renderer/core/layout/svg/layout_svg_resource_masker.h"
#include "third_party/blink/renderer/core/layout/svg/layout_svg_text.h" #include "third_party/blink/renderer/core/layout/svg/layout_svg_text.h"
#include "third_party/blink/renderer/core/layout/svg/svg_layout_support.h" #include "third_party/blink/renderer/core/layout/svg/svg_layout_support.h"
...@@ -509,9 +510,10 @@ bool LayoutSVGRoot::NodeAtPoint(HitTestResult& result, ...@@ -509,9 +510,10 @@ bool LayoutSVGRoot::NodeAtPoint(HitTestResult& result,
const HitTestLocation& location_in_container, const HitTestLocation& location_in_container,
const LayoutPoint& accumulated_offset, const LayoutPoint& accumulated_offset,
HitTestAction hit_test_action) { HitTestAction hit_test_action) {
LayoutPoint point_in_parent = LayoutPoint adjusted_location = accumulated_offset + Location();
location_in_container.Point() - ToLayoutSize(accumulated_offset);
LayoutPoint point_in_border_box = point_in_parent - ToLayoutSize(Location()); HitTestLocation local_border_box_location(location_in_container,
ToLayoutSize(-adjusted_location));
// Only test SVG content if the point is in our content box, or in case we // Only test SVG content if the point is in our content box, or in case we
// don't clip to the viewport, the visual overflow rect. // don't clip to the viewport, the visual overflow rect.
...@@ -519,23 +521,44 @@ bool LayoutSVGRoot::NodeAtPoint(HitTestResult& result, ...@@ -519,23 +521,44 @@ bool LayoutSVGRoot::NodeAtPoint(HitTestResult& result,
// supported by nodeAtFloatPoint. // supported by nodeAtFloatPoint.
bool skip_children = (result.GetHitTestRequest().GetStopNode() == this); bool skip_children = (result.GetHitTestRequest().GetStopNode() == this);
if (!skip_children && if (!skip_children &&
(PhysicalContentBoxRect().Contains(point_in_border_box) || (local_border_box_location.Intersects(PhysicalContentBoxRect()) ||
(!ShouldApplyViewportClip() && (!ShouldApplyViewportClip() &&
VisualOverflowRect().Contains(point_in_border_box)))) { local_border_box_location.Intersects(VisualOverflowRect())))) {
const AffineTransform& local_to_parent_transform = const AffineTransform& local_to_border_box_transform =
LocalToSVGParentTransform(); LocalToBorderBoxTransform();
if (local_to_parent_transform.IsInvertible()) { if (local_to_border_box_transform.IsInvertible()) {
FloatPoint local_point = local_to_parent_transform.Inverse().MapPoint( FloatPoint local_point = local_to_border_box_transform.Inverse().MapPoint(
FloatPoint(point_in_parent)); local_border_box_location.TransformedPoint());
base::Optional<HitTestLocation> local_location;
if (location_in_container.IsRectBasedTest()) {
FloatQuad quad_in_container =
local_border_box_location.TransformedRect();
local_location.emplace(
local_point,
local_to_border_box_transform.Inverse().MapQuad(quad_in_container));
} else {
local_location.emplace(local_point);
}
for (LayoutObject* child = LastChild(); child; for (LayoutObject* child = LastChild(); child;
child = child->PreviousSibling()) { child = child->PreviousSibling()) {
// FIXME: nodeAtFloatPoint() doesn't handle rect-based hit tests yet. bool found = false;
if (child->NodeAtFloatPoint(result, local_point, hit_test_action)) { if (child->IsSVGForeignObject()) {
UpdateHitTestResult(result, point_in_border_box); found = ToLayoutSVGForeignObject(child)->NodeAtPointFromSVG(
result, *local_location, LayoutPoint(), hit_test_action);
} else {
found = child->NodeAtPoint(result, *local_location, LayoutPoint(),
hit_test_action);
}
if (found) {
UpdateHitTestResult(result, local_border_box_location.Point());
if (result.AddNodeToListBasedTestResult( if (result.AddNodeToListBasedTestResult(
child->GetNode(), location_in_container) == kStopHitTesting) child->GetNode(), location_in_container) == kStopHitTesting) {
return true; return true;
}
} }
} }
} }
...@@ -556,7 +579,7 @@ bool LayoutSVGRoot::NodeAtPoint(HitTestResult& result, ...@@ -556,7 +579,7 @@ bool LayoutSVGRoot::NodeAtPoint(HitTestResult& result,
// detect these hits anymore. // detect these hits anymore.
LayoutRect bounds_rect(accumulated_offset + Location(), Size()); LayoutRect bounds_rect(accumulated_offset + Location(), Size());
if (location_in_container.Intersects(bounds_rect)) { if (location_in_container.Intersects(bounds_rect)) {
UpdateHitTestResult(result, point_in_border_box); UpdateHitTestResult(result, local_border_box_location.Point());
if (result.AddNodeToListBasedTestResult(GetNode(), location_in_container, if (result.AddNodeToListBasedTestResult(GetNode(), location_in_container,
bounds_rect) == kStopHitTesting) bounds_rect) == kStopHitTesting)
return true; return true;
......
...@@ -132,4 +132,33 @@ TEST_F(LayoutSVGRootTest, ...@@ -132,4 +132,33 @@ TEST_F(LayoutSVGRootTest,
EXPECT_FALSE(root.PaintedOutputOfObjectHasNoEffectRegardlessOfSize()); EXPECT_FALSE(root.PaintedOutputOfObjectHasNoEffectRegardlessOfSize());
} }
TEST_F(LayoutSVGRootTest, RectBasedHitTestPartialOverlap) {
SetBodyInnerHTML(R"HTML(
<style>body { margin: 0 }</style>
<svg id='svg' style='width: 300px; height: 300px; position: relative;
top: 200px; left: 200px;'>
</svg>
)HTML");
const auto& svg = *GetDocument().getElementById("svg");
const auto& body = *GetDocument().body();
// This is the center of the rect-based hit test below.
EXPECT_EQ(body, *HitTest(150, 150));
EXPECT_EQ(svg, *HitTest(200, 200));
// The center of this rect does not overlap the SVG element, but the
// rect itself does.
auto results = RectBasedHitTest(LayoutRect(0, 0, 300, 300));
int count = 0;
EXPECT_EQ(2u, results.size());
for (auto result : results) {
Node* node = result.Get();
if (node == svg || node == body)
count++;
}
EXPECT_EQ(2, count);
}
} // namespace blink } // namespace blink
...@@ -347,27 +347,29 @@ void LayoutSVGShape::Paint(const PaintInfo& paint_info) const { ...@@ -347,27 +347,29 @@ void LayoutSVGShape::Paint(const PaintInfo& paint_info) const {
SVGShapePainter(*this).Paint(paint_info); SVGShapePainter(*this).Paint(paint_info);
} }
bool LayoutSVGShape::NodeAtFloatPoint(HitTestResult& result, bool LayoutSVGShape::NodeAtPoint(HitTestResult& result,
const FloatPoint& point_in_parent, const HitTestLocation& location_in_parent,
HitTestAction hit_test_action) { const LayoutPoint& accumulated_offset,
HitTestAction hit_test_action) {
DCHECK_EQ(accumulated_offset, LayoutPoint());
// We only draw in the foreground phase, so we only hit-test then. // We only draw in the foreground phase, so we only hit-test then.
if (hit_test_action != kHitTestForeground) if (hit_test_action != kHitTestForeground)
return false; return false;
FloatPoint local_point; HitTestLocation local_location;
if (!SVGLayoutSupport::TransformToUserSpaceAndCheckClipping( if (!SVGLayoutSupport::TransformToUserSpaceAndCheckClipping(
*this, LocalToSVGParentTransform(), point_in_parent, local_point)) *this, LocalToSVGParentTransform(), location_in_parent,
local_location))
return false; return false;
PointerEventsHitRules hit_rules( PointerEventsHitRules hit_rules(
PointerEventsHitRules::SVG_GEOMETRY_HITTESTING, PointerEventsHitRules::SVG_GEOMETRY_HITTESTING,
result.GetHitTestRequest(), StyleRef().PointerEvents()); result.GetHitTestRequest(), StyleRef().PointerEvents());
if (NodeAtFloatPointInternal(result.GetHitTestRequest(), local_point, if (NodeAtPointInternal(result.GetHitTestRequest(), local_location,
hit_rules)) { hit_rules)) {
const LayoutPoint& local_layout_point = LayoutPoint(local_point); const LayoutPoint local_layout_point(local_location.TransformedPoint());
UpdateHitTestResult(result, local_layout_point); UpdateHitTestResult(result, local_layout_point);
HitTestLocation location(local_layout_point); if (result.AddNodeToListBasedTestResult(GetElement(), local_location) ==
if (result.AddNodeToListBasedTestResult(GetElement(), location) ==
kStopHitTesting) kStopHitTesting)
return true; return true;
} }
...@@ -375,26 +377,30 @@ bool LayoutSVGShape::NodeAtFloatPoint(HitTestResult& result, ...@@ -375,26 +377,30 @@ bool LayoutSVGShape::NodeAtFloatPoint(HitTestResult& result,
return false; return false;
} }
bool LayoutSVGShape::NodeAtFloatPointInternal(const HitTestRequest& request, bool LayoutSVGShape::NodeAtPointInternal(const HitTestRequest& request,
const FloatPoint& local_point, const HitTestLocation& local_location,
PointerEventsHitRules hit_rules) { PointerEventsHitRules hit_rules) {
const ComputedStyle& style = StyleRef(); const ComputedStyle& style = StyleRef();
if (hit_rules.require_visible && style.Visibility() != EVisibility::kVisible) if (hit_rules.require_visible && style.Visibility() != EVisibility::kVisible)
return false; return false;
if (hit_rules.can_hit_bounding_box && if (hit_rules.can_hit_bounding_box &&
ObjectBoundingBox().Contains(local_point)) local_location.Intersects(ObjectBoundingBox()))
return true; return true;
// TODO(chrishtr): support rect-based intersections in the cases below.
const SVGComputedStyle& svg_style = style.SvgStyle(); const SVGComputedStyle& svg_style = style.SvgStyle();
if (hit_rules.can_hit_stroke && if (hit_rules.can_hit_stroke &&
(svg_style.HasStroke() || !hit_rules.require_stroke) && (svg_style.HasStroke() || !hit_rules.require_stroke) &&
StrokeContains(local_point, hit_rules.require_stroke)) StrokeContains(local_location.TransformedPoint(),
hit_rules.require_stroke))
return true; return true;
WindRule fill_rule = svg_style.FillRule(); WindRule fill_rule = svg_style.FillRule();
if (request.SvgClipContent()) if (request.SvgClipContent())
fill_rule = svg_style.ClipRule(); fill_rule = svg_style.ClipRule();
if (hit_rules.can_hit_fill && if (hit_rules.can_hit_fill &&
(svg_style.HasFill() || !hit_rules.require_fill) && (svg_style.HasFill() || !hit_rules.require_fill) &&
FillContains(local_point, hit_rules.require_fill, fill_rule)) FillContains(local_location.TransformedPoint(), hit_rules.require_fill,
fill_rule))
return true; return true;
return false; return false;
} }
......
...@@ -65,9 +65,9 @@ class LayoutSVGShape : public LayoutSVGModelObject { ...@@ -65,9 +65,9 @@ class LayoutSVGShape : public LayoutSVGModelObject {
void SetNeedsBoundariesUpdate() final { needs_boundaries_update_ = true; } void SetNeedsBoundariesUpdate() final { needs_boundaries_update_ = true; }
void SetNeedsTransformUpdate() final { needs_transform_update_ = true; } void SetNeedsTransformUpdate() final { needs_transform_update_ = true; }
bool NodeAtFloatPointInternal(const HitTestRequest&, bool NodeAtPointInternal(const HitTestRequest&,
const FloatPoint&, const HitTestLocation&,
PointerEventsHitRules); PointerEventsHitRules);
Path& GetPath() const { Path& GetPath() const {
DCHECK(path_); DCHECK(path_);
...@@ -157,9 +157,10 @@ class LayoutSVGShape : public LayoutSVGModelObject { ...@@ -157,9 +157,10 @@ class LayoutSVGShape : public LayoutSVGModelObject {
void UpdateLayout() final; void UpdateLayout() final;
void Paint(const PaintInfo&) const final; void Paint(const PaintInfo&) const final;
bool NodeAtFloatPoint(HitTestResult&, bool NodeAtPoint(HitTestResult&,
const FloatPoint& point_in_parent, const HitTestLocation& location_in_parent,
HitTestAction) final; const LayoutPoint& accumulated_offset,
HitTestAction) override;
FloatRect StrokeBoundingBox() const final { return stroke_bounding_box_; } FloatRect StrokeBoundingBox() const final { return stroke_bounding_box_; }
FloatRect CalculateObjectBoundingBox() const; FloatRect CalculateObjectBoundingBox() const;
......
...@@ -305,31 +305,33 @@ RootInlineBox* LayoutSVGText::CreateRootInlineBox() { ...@@ -305,31 +305,33 @@ RootInlineBox* LayoutSVGText::CreateRootInlineBox() {
return box; return box;
} }
bool LayoutSVGText::NodeAtFloatPoint(HitTestResult& result, bool LayoutSVGText::NodeAtPoint(HitTestResult& result,
const FloatPoint& point_in_parent, const HitTestLocation& location_in_parent,
HitTestAction hit_test_action) { const LayoutPoint& accumulated_offset,
HitTestAction hit_test_action) {
DCHECK_EQ(accumulated_offset, LayoutPoint());
// We only draw in the foreground phase, so we only hit-test then. // We only draw in the foreground phase, so we only hit-test then.
if (hit_test_action != kHitTestForeground) if (hit_test_action != kHitTestForeground)
return false; return false;
FloatPoint local_point; HitTestLocation local_location;
if (!SVGLayoutSupport::TransformToUserSpaceAndCheckClipping( if (!SVGLayoutSupport::TransformToUserSpaceAndCheckClipping(
*this, LocalToSVGParentTransform(), point_in_parent, local_point)) *this, LocalToSVGParentTransform(), location_in_parent,
local_location))
return false; return false;
HitTestLocation hit_test_location(local_point); if (LayoutBlock::NodeAtPoint(result, local_location, accumulated_offset,
if (LayoutBlock::NodeAtPoint(result, hit_test_location, LayoutPoint(),
hit_test_action)) hit_test_action))
return true; return true;
// Consider the bounding box if requested. // Consider the bounding box if requested.
if (StyleRef().PointerEvents() == EPointerEvents::kBoundingBox) { if (StyleRef().PointerEvents() == EPointerEvents::kBoundingBox) {
if (IsObjectBoundingBoxValid() && if (IsObjectBoundingBoxValid() &&
ObjectBoundingBox().Contains(local_point)) { local_location.Intersects(ObjectBoundingBox())) {
const LayoutPoint& local_layout_point = LayoutPoint(local_point); const LayoutPoint& local_layout_point =
LayoutPoint(local_location.TransformedPoint());
UpdateHitTestResult(result, local_layout_point); UpdateHitTestResult(result, local_layout_point);
HitTestLocation location(local_layout_point); if (result.AddNodeToListBasedTestResult(GetElement(), local_location) ==
if (result.AddNodeToListBasedTestResult(GetElement(), location) ==
kStopHitTesting) kStopHitTesting)
return true; return true;
} }
......
...@@ -72,9 +72,10 @@ class LayoutSVGText final : public LayoutSVGBlock { ...@@ -72,9 +72,10 @@ class LayoutSVGText final : public LayoutSVGBlock {
} }
void Paint(const PaintInfo&) const override; void Paint(const PaintInfo&) const override;
bool NodeAtFloatPoint(HitTestResult&, bool NodeAtPoint(HitTestResult&,
const FloatPoint& point_in_parent, const HitTestLocation& location_in_parent,
HitTestAction) override; const LayoutPoint& accumulated_offset,
HitTestAction) override;
PositionWithAffinity PositionForPoint(const LayoutPoint&) const override; PositionWithAffinity PositionForPoint(const LayoutPoint&) const override;
void UpdateLayout() override; void UpdateLayout() override;
......
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/core/layout/layout_geometry_map.h"
#include "third_party/blink/renderer/core/paint/paint_layer.h"
#include "third_party/blink/renderer/core/testing/core_unit_test_helper.h"
namespace blink {
class LayoutSVGTextTest : public RenderingTest {
public:
LayoutSVGTextTest() : RenderingTest(SingleChildLocalFrameClient::Create()) {}
};
TEST_F(LayoutSVGTextTest, RectBasedHitTest) {
SetBodyInnerHTML(R"HTML(
<style>body { margin: 0 }</style>
<svg id=svg width="300" height="300">
<a id="link">
<text id="text" y="20">text</text>
</a>
</svg>
)HTML");
const auto& svg = *GetDocument().getElementById("svg");
const auto& text = *GetDocument().getElementById("text")->firstChild();
// Rect based hit testing
auto results = RectBasedHitTest(LayoutRect(0, 0, 300, 300));
int count = 0;
EXPECT_EQ(2u, results.size());
for (auto result : results) {
Node* node = result.Get();
if (node == svg || node == text)
count++;
}
EXPECT_EQ(2, count);
}
} // namespace blink
...@@ -75,16 +75,18 @@ SVGTransformChange LayoutSVGViewportContainer::CalculateLocalTransform() { ...@@ -75,16 +75,18 @@ SVGTransformChange LayoutSVGViewportContainer::CalculateLocalTransform() {
return change_detector.ComputeChange(local_to_parent_transform_); return change_detector.ComputeChange(local_to_parent_transform_);
} }
bool LayoutSVGViewportContainer::NodeAtFloatPoint( bool LayoutSVGViewportContainer::NodeAtPoint(
HitTestResult& result, HitTestResult& result,
const FloatPoint& point_in_parent, const HitTestLocation& location_in_parent,
const LayoutPoint& accumulated_offset,
HitTestAction action) { HitTestAction action) {
// Respect the viewport clip which is in parent coordinates. // Respect the viewport clip which is in parent coordinates.
if (SVGLayoutSupport::IsOverflowHidden(*this)) { if (SVGLayoutSupport::IsOverflowHidden(*this)) {
if (!viewport_.Contains(point_in_parent)) if (!location_in_parent.Intersects(viewport_))
return false; return false;
} }
return LayoutSVGContainer::NodeAtFloatPoint(result, point_in_parent, action); return LayoutSVGContainer::NodeAtPoint(result, location_in_parent,
accumulated_offset, action);
} }
void LayoutSVGViewportContainer::StyleDidChange( void LayoutSVGViewportContainer::StyleDidChange(
......
...@@ -56,9 +56,10 @@ class LayoutSVGViewportContainer final : public LayoutSVGContainer { ...@@ -56,9 +56,10 @@ class LayoutSVGViewportContainer final : public LayoutSVGContainer {
SVGTransformChange CalculateLocalTransform() override; SVGTransformChange CalculateLocalTransform() override;
bool NodeAtFloatPoint(HitTestResult&, bool NodeAtPoint(HitTestResult&,
const FloatPoint& point_in_parent, const HitTestLocation& location_in_parent,
HitTestAction) override; const LayoutPoint& accumulated_offset,
HitTestAction) final;
void StyleDidChange(StyleDifference, const ComputedStyle* old_style) override; void StyleDidChange(StyleDifference, const ComputedStyle* old_style) override;
......
...@@ -308,10 +308,9 @@ bool SVGInlineTextBox::NodeAtPoint(HitTestResult& result, ...@@ -308,10 +308,9 @@ bool SVGInlineTextBox::NodeAtPoint(HitTestResult& result,
DCHECK(line_layout_item.ScalingFactor()); DCHECK(line_layout_item.ScalingFactor());
float baseline = font_data->GetFontMetrics().FloatAscent() / float baseline = font_data->GetFontMetrics().FloatAscent() /
line_layout_item.ScalingFactor(); line_layout_item.ScalingFactor();
FloatPoint float_location = FloatPoint(location_in_container.Point());
for (const SVGTextFragment& fragment : text_fragments_) { for (const SVGTextFragment& fragment : text_fragments_) {
FloatQuad fragment_quad = fragment.BoundingQuad(baseline); FloatQuad fragment_quad = fragment.BoundingQuad(baseline);
if (fragment_quad.ContainsPoint(float_location)) { if (location_in_container.Intersects(fragment_quad)) {
line_layout_item.UpdateHitTestResult( line_layout_item.UpdateHitTestResult(
result, result,
location_in_container.Point() - ToLayoutSize(accumulated_offset)); location_in_container.Point() - ToLayoutSize(accumulated_offset));
......
...@@ -432,12 +432,21 @@ bool SVGLayoutSupport::PointInClippingArea(const LayoutObject& object, ...@@ -432,12 +432,21 @@ bool SVGLayoutSupport::PointInClippingArea(const LayoutObject& object,
bool SVGLayoutSupport::TransformToUserSpaceAndCheckClipping( bool SVGLayoutSupport::TransformToUserSpaceAndCheckClipping(
const LayoutObject& object, const LayoutObject& object,
const AffineTransform& local_transform, const AffineTransform& local_transform,
const FloatPoint& point_in_parent, const HitTestLocation& location_in_parent,
FloatPoint& local_point) { HitTestLocation& local_location) {
if (!local_transform.IsInvertible()) if (!local_transform.IsInvertible())
return false; return false;
local_point = local_transform.Inverse().MapPoint(point_in_parent); const AffineTransform inverse = local_transform.Inverse();
return PointInClippingArea(object, local_point); if (location_in_parent.IsRectBasedTest()) {
local_location =
HitTestLocation(inverse.MapPoint(location_in_parent.TransformedPoint()),
inverse.MapQuad(location_in_parent.TransformedRect()));
} else {
local_location = HitTestLocation(
inverse.MapPoint(location_in_parent.TransformedPoint()));
}
return PointInClippingArea(object, local_location.TransformedPoint());
} }
DashArray SVGLayoutSupport::ResolveSVGDashArray( DashArray SVGLayoutSupport::ResolveSVGDashArray(
......
...@@ -82,8 +82,8 @@ class CORE_EXPORT SVGLayoutSupport { ...@@ -82,8 +82,8 @@ class CORE_EXPORT SVGLayoutSupport {
static bool TransformToUserSpaceAndCheckClipping( static bool TransformToUserSpaceAndCheckClipping(
const LayoutObject&, const LayoutObject&,
const AffineTransform& local_transform, const AffineTransform& local_transform,
const FloatPoint& point_in_parent, const HitTestLocation& location_in_parent,
FloatPoint& local_point); HitTestLocation& local_location);
static void ComputeContainerBoundingBoxes(const LayoutObject* container, static void ComputeContainerBoundingBoxes(const LayoutObject* container,
FloatRect& object_bounding_box, FloatRect& object_bounding_box,
......
...@@ -97,8 +97,9 @@ bool SVGGeometryElement::isPointInFill(SVGPointTearOff* point) const { ...@@ -97,8 +97,9 @@ bool SVGGeometryElement::isPointInFill(SVGPointTearOff* point) const {
PointerEventsHitRules::SVG_GEOMETRY_HITTESTING, request, PointerEventsHitRules::SVG_GEOMETRY_HITTESTING, request,
GetLayoutObject()->StyleRef().PointerEvents()); GetLayoutObject()->StyleRef().PointerEvents());
hit_rules.can_hit_stroke = false; hit_rules.can_hit_stroke = false;
HitTestLocation location(point->Target()->Value());
return ToLayoutSVGShape(GetLayoutObject()) return ToLayoutSVGShape(GetLayoutObject())
->NodeAtFloatPointInternal(request, point->Target()->Value(), hit_rules); ->NodeAtPointInternal(request, location, hit_rules);
} }
bool SVGGeometryElement::isPointInStroke(SVGPointTearOff* point) const { bool SVGGeometryElement::isPointInStroke(SVGPointTearOff* point) const {
...@@ -114,8 +115,9 @@ bool SVGGeometryElement::isPointInStroke(SVGPointTearOff* point) const { ...@@ -114,8 +115,9 @@ bool SVGGeometryElement::isPointInStroke(SVGPointTearOff* point) const {
PointerEventsHitRules::SVG_GEOMETRY_HITTESTING, request, PointerEventsHitRules::SVG_GEOMETRY_HITTESTING, request,
GetLayoutObject()->StyleRef().PointerEvents()); GetLayoutObject()->StyleRef().PointerEvents());
hit_rules.can_hit_fill = false; hit_rules.can_hit_fill = false;
HitTestLocation location(point->Target()->Value());
return ToLayoutSVGShape(GetLayoutObject()) return ToLayoutSVGShape(GetLayoutObject())
->NodeAtFloatPointInternal(request, point->Target()->Value(), hit_rules); ->NodeAtPointInternal(request, location, hit_rules);
} }
Path SVGGeometryElement::ToClipPath() const { Path SVGGeometryElement::ToClipPath() const {
......
...@@ -52,6 +52,17 @@ const Node* RenderingTest::HitTest(int x, int y) { ...@@ -52,6 +52,17 @@ const Node* RenderingTest::HitTest(int x, int y) {
return result.InnerNode(); return result.InnerNode();
} }
HitTestResult::NodeSet RenderingTest::RectBasedHitTest(LayoutRect rect) {
HitTestLocation location(rect);
HitTestResult result(
HitTestRequest(HitTestRequest::kReadOnly | HitTestRequest::kActive |
HitTestRequest::kAllowChildFrameContent |
HitTestRequest::kListBased),
location);
GetLayoutView().HitTest(location, result);
return result.ListBasedTestResult();
}
void RenderingTest::SetUp() { void RenderingTest::SetUp() {
Page::PageClients page_clients; Page::PageClients page_clients;
FillWithEmptyClients(page_clients); FillWithEmptyClients(page_clients);
......
...@@ -79,6 +79,7 @@ class RenderingTest : public PageTestBase, public UseMockScrollbarSettings { ...@@ -79,6 +79,7 @@ class RenderingTest : public PageTestBase, public UseMockScrollbarSettings {
explicit RenderingTest(LocalFrameClient* = nullptr); explicit RenderingTest(LocalFrameClient* = nullptr);
const Node* HitTest(int x, int y); const Node* HitTest(int x, int y);
HitTestResult::NodeSet RectBasedHitTest(LayoutRect rect);
protected: protected:
void SetUp() override; void SetUp() override;
......
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