Commit e2da4910 authored by Yi Gu's avatar Yi Gu Committed by Commit Bot

[VizHitTesting] Fix hit test result mismatch due to iframe's padding

Input event on the padding / border of an iframe should be claimed by
its parent frame. See example here: https://jsbin.com/xozuwil/quiet.

However, clicking on the padding is considered as clicking the iframe
according to blink hit test. When VizHitTesting asks blink for hit test
target, it gets the iframe which is inconsistent with VizHitTesting
result where the parent frame is considered as the target.

This patch takes the padding and border into account when asking blink
for the hit test result and only accepts iframe as the target if the
hit test point is inside the content box of the iframe.

Note that fixing the inconsistency between blink hit test and viz hite
test is tracked via crbug.com/753124.

Bug: 977180
Change-Id: Ia0c36b68ef0428a19f7ae94f2ae9bf575f55f0b2
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1699589
Commit-Queue: Yi Gu <yigu@chromium.org>
Reviewed-by: default avatarMike Pinkerton <pinkerton@chromium.org>
Reviewed-by: default avatarSadrul Chowdhury <sadrul@chromium.org>
Cr-Commit-Position: refs/heads/master@{#678499}
parent 63749f86
...@@ -6221,6 +6221,91 @@ IN_PROC_BROWSER_TEST_P(SitePerProcessHitTestBrowserTest, HitTestNestedFrames) { ...@@ -6221,6 +6221,91 @@ IN_PROC_BROWSER_TEST_P(SitePerProcessHitTestBrowserTest, HitTestNestedFrames) {
} }
} }
IN_PROC_BROWSER_TEST_P(SitePerProcessHitTestBrowserTest,
HitTestOOPIFWithPaddingAndBorder) {
GURL main_url(embedded_test_server()->GetURL(
"/frame_tree/oopif_with_padding_and_border.html"));
EXPECT_TRUE(NavigateToURL(shell(), main_url));
FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
->GetFrameTree()
->root();
ASSERT_EQ(1U, root->child_count());
RenderWidgetHostViewBase* rwhv_parent =
static_cast<RenderWidgetHostViewBase*>(
root->current_frame_host()->GetRenderWidgetHost()->GetView());
FrameTreeNode* child_node = root->child_at(0);
RenderWidgetHostViewBase* rwhv_child = static_cast<RenderWidgetHostViewBase*>(
child_node->current_frame_host()->GetRenderWidgetHost()->GetView());
WaitForHitTestDataOrChildSurfaceReady(child_node->current_frame_host());
// Layout of the loaded page:
//
// |(0, 0)----------------------------|
// | border |
// | |(20, 20)----------------| |
// | | padding | |
// | | |(40, 40) -------| | |
// | | | | | |
// | | | content box | | |
// | | | | | |
// | | |----------------| | |
// | | | |
// |----|------------------------|----|(280, 280)
//
// Clicks on the padding or border should be handled by the root while
// clicks on the content box should be handled by the iframe.
const gfx::PointF child_origin =
rwhv_child->TransformPointToRootCoordSpaceF(gfx::PointF());
{
gfx::PointF point_in_border = child_origin + gfx::Vector2dF(-30, -30);
base::RunLoop run_loop;
viz::FrameSinkId received_frame_sink_id;
root->current_frame_host()->GetInputTargetClient()->FrameSinkIdAt(
point_in_border, 0,
base::BindLambdaForTesting(
[&](const viz::FrameSinkId& id, const gfx::PointF& point) {
received_frame_sink_id = id;
run_loop.Quit();
}));
run_loop.Run();
EXPECT_EQ(rwhv_parent->GetFrameSinkId(), received_frame_sink_id);
}
{
gfx::PointF point_in_padding = child_origin + gfx::Vector2dF(-10, -10);
base::RunLoop run_loop;
viz::FrameSinkId received_frame_sink_id;
root->current_frame_host()->GetInputTargetClient()->FrameSinkIdAt(
point_in_padding, 0,
base::BindLambdaForTesting(
[&](const viz::FrameSinkId& id, const gfx::PointF& point) {
received_frame_sink_id = id;
run_loop.Quit();
}));
run_loop.Run();
EXPECT_EQ(rwhv_parent->GetFrameSinkId(), received_frame_sink_id);
}
{
gfx::PointF point_in_content_box = child_origin + gfx::Vector2dF(10, 10);
base::RunLoop run_loop;
viz::FrameSinkId received_frame_sink_id;
root->current_frame_host()->GetInputTargetClient()->FrameSinkIdAt(
point_in_content_box, 0,
base::BindLambdaForTesting(
[&](const viz::FrameSinkId& id, const gfx::PointF& point) {
received_frame_sink_id = id;
run_loop.Quit();
}));
run_loop.Run();
EXPECT_EQ(rwhv_child->GetFrameSinkId(), received_frame_sink_id);
}
}
class SitePerProcessHitTestDataGenerationBrowserTest class SitePerProcessHitTestDataGenerationBrowserTest
: public SitePerProcessHitTestBrowserTest { : public SitePerProcessHitTestBrowserTest {
public: public:
......
...@@ -186,13 +186,18 @@ blink::WebCoalescedInputEvent GetCoalescedWebPointerEventForTouch( ...@@ -186,13 +186,18 @@ blink::WebCoalescedInputEvent GetCoalescedWebPointerEventForTouch(
predicted_pointer_events); predicted_pointer_events);
} }
viz::FrameSinkId GetRemoteFrameSinkId(const blink::WebNode& node) { viz::FrameSinkId GetRemoteFrameSinkId(const blink::WebHitTestResult& result) {
const blink::WebNode& node = result.GetNode();
DCHECK(!node.IsNull());
blink::WebFrame* result_frame = blink::WebFrame::FromFrameOwnerElement(node); blink::WebFrame* result_frame = blink::WebFrame::FromFrameOwnerElement(node);
if (result_frame && result_frame->IsWebRemoteFrame()) { if (result_frame && result_frame->IsWebRemoteFrame()) {
blink::WebRemoteFrame* remote_frame = result_frame->ToWebRemoteFrame(); blink::WebRemoteFrame* remote_frame = result_frame->ToWebRemoteFrame();
if (remote_frame->IsIgnoredForHitTest()) if (remote_frame->IsIgnoredForHitTest())
return viz::FrameSinkId(); return viz::FrameSinkId();
if (!result.ContentBoxContainsPoint())
return viz::FrameSinkId();
return RenderFrameProxy::FromWebFrame(remote_frame)->frame_sink_id(); return RenderFrameProxy::FromWebFrame(remote_frame)->frame_sink_id();
} }
auto* plugin = BrowserPlugin::GetFromNode(node); auto* plugin = BrowserPlugin::GetFromNode(node);
...@@ -242,7 +247,7 @@ viz::FrameSinkId RenderWidgetInputHandler::GetFrameSinkIdAtPoint( ...@@ -242,7 +247,7 @@ viz::FrameSinkId RenderWidgetInputHandler::GetFrameSinkIdAtPoint(
widget_->routing_id()); widget_->routing_id());
} }
viz::FrameSinkId frame_sink_id = GetRemoteFrameSinkId(result_node); viz::FrameSinkId frame_sink_id = GetRemoteFrameSinkId(result);
if (frame_sink_id.is_valid()) { if (frame_sink_id.is_valid()) {
*local_point = gfx::PointF(result.LocalPointWithoutContentBoxOffset()); *local_point = gfx::PointF(result.LocalPointWithoutContentBoxOffset());
if (widget_->compositor_deps()->IsUseZoomForDSFEnabled()) { if (widget_->compositor_deps()->IsUseZoomForDSFEnabled()) {
...@@ -253,8 +258,8 @@ viz::FrameSinkId RenderWidgetInputHandler::GetFrameSinkIdAtPoint( ...@@ -253,8 +258,8 @@ viz::FrameSinkId RenderWidgetInputHandler::GetFrameSinkIdAtPoint(
} }
// Return the FrameSinkId for the current widget if the point did not hit // Return the FrameSinkId for the current widget if the point did not hit
// test to a remote frame, or the remote frame doesn't have a valid // test to a remote frame, or the point is outside of the remote frame's
// FrameSinkId yet. // content box, or the remote frame doesn't have a valid FrameSinkId yet.
return viz::FrameSinkId(RenderThread::Get()->GetClientId(), return viz::FrameSinkId(RenderThread::Get()->GetClientId(),
widget_->routing_id()); widget_->routing_id());
} }
......
<!DOCTYPE html>
<style>
body {
margin: 0px;
}
iframe {
position: absolute;
width: 200px;
height: 200px;
border: 20px solid;
padding: 20px;
}
</style>
<body>
<iframe src="/cross-site/baz.com/title1.html"></iframe>
</body>
...@@ -60,6 +60,9 @@ class WebHitTestResult { ...@@ -60,6 +60,9 @@ class WebHitTestResult {
// ContentBoxOffset removed if the node has box layout. // ContentBoxOffset removed if the node has box layout.
BLINK_EXPORT WebPoint LocalPointWithoutContentBoxOffset() const; BLINK_EXPORT WebPoint LocalPointWithoutContentBoxOffset() const;
// Returns whether the content box contains the hit test point.
BLINK_EXPORT bool ContentBoxContainsPoint() const;
// If a link (eg. anchor or area tag) is hit, return the element. // If a link (eg. anchor or area tag) is hit, return the element.
// Return null otheriwse. // Return null otheriwse.
BLINK_EXPORT WebElement UrlElement() const; BLINK_EXPORT WebElement UrlElement() const;
......
...@@ -76,6 +76,16 @@ WebPoint WebHitTestResult::LocalPointWithoutContentBoxOffset() const { ...@@ -76,6 +76,16 @@ WebPoint WebHitTestResult::LocalPointWithoutContentBoxOffset() const {
return local_point; return local_point;
} }
bool WebHitTestResult::ContentBoxContainsPoint() const {
LayoutObject* object = private_->Result().GetLayoutObject();
DCHECK(object);
if (!object->IsBox())
return false;
IntPoint local_point = RoundedIntPoint(private_->Result().LocalPoint());
return ToLayoutBox(object)->ComputedCSSContentBoxRect().Contains(local_point);
}
WebElement WebHitTestResult::UrlElement() const { WebElement WebHitTestResult::UrlElement() const {
return WebElement(private_->Result().URLElement()); return WebElement(private_->Result().URLElement());
} }
......
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