Commit 827f22ba authored by Charlie Harrison's avatar Charlie Harrison Committed by Commit Bot

Large cursor fallback: ensure correct coordinate space for OOPIFs

A previous change ensured that large custom cursors > 32x32
would be dropped if they are not fully contained with the visual
viewport. However, the computation did not account properly for OOPIFs,
where cursor coordinates were not adjusted to the viewport offset.
This CL further adjusts the cursor rect by translating it to the root
view's coordinate space via LocalToAncestorPoint, before
checking for containment within the visual viewport.

Bug: 1099276
Change-Id: I0a03e7cc249cd785f9e76f931cfc7931b127d56b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2278597
Auto-Submit: Charlie Harrison <csharrison@chromium.org>
Reviewed-by: default avatarKen Buchanan <kenrb@chromium.org>
Reviewed-by: default avatarStefan Zager <szager@chromium.org>
Reviewed-by: default avatarDavid Bokan <bokan@chromium.org>
Commit-Queue: Charlie Harrison <csharrison@chromium.org>
Cr-Commit-Position: refs/heads/master@{#790122}
parent fccebf91
...@@ -4225,14 +4225,18 @@ class SetCursorInterceptor ...@@ -4225,14 +4225,18 @@ class SetCursorInterceptor
void SetCursor(const ui::Cursor& cursor) override { void SetCursor(const ui::Cursor& cursor) override {
GetForwardingInterface()->SetCursor(cursor); GetForwardingInterface()->SetCursor(cursor);
cursor_ = cursor;
run_loop_.Quit(); run_loop_.Quit();
} }
void Wait() { run_loop_.Run(); } void Wait() { run_loop_.Run(); }
base::Optional<ui::Cursor> cursor() const { return cursor_; }
private: private:
base::RunLoop run_loop_; base::RunLoop run_loop_;
RenderWidgetHostImpl* render_widget_host_; RenderWidgetHostImpl* render_widget_host_;
base::Optional<ui::Cursor> cursor_;
}; };
// Verify that we receive a mouse cursor update message when we mouse over // Verify that we receive a mouse cursor update message when we mouse over
...@@ -4331,6 +4335,82 @@ IN_PROC_BROWSER_TEST_F(SitePerProcessHighDPIHitTestBrowserTest, ...@@ -4331,6 +4335,82 @@ IN_PROC_BROWSER_TEST_F(SitePerProcessHighDPIHitTestBrowserTest,
CursorUpdateReceivedFromCrossSiteIframeHelper(shell(), CursorUpdateReceivedFromCrossSiteIframeHelper(shell(),
embedded_test_server()); embedded_test_server());
} }
// Regression test for https://crbug.com/1099276. An OOPIF at a negative offset
// from the main document should not allow large cursors to intersect browser
// UI.
IN_PROC_BROWSER_TEST_F(SitePerProcessHitTestBrowserTest,
LargeCursorRemovedInOffsetOOPIF) {
GURL url(R"(data:text/html,
<iframe id='iframe'
style ='position:absolute; top: -100px'
width=1000px height=1000px>
</iframe>)");
EXPECT_TRUE(NavigateToURL(shell(), url));
// The large-cursor.html document has a custom cursor that is 120x120 with a
// hotspot on the bottom right corner.
NavigateIframeToURL(shell()->web_contents(), "iframe",
embedded_test_server()->GetURL("/large-cursor.html"));
auto* web_contents = static_cast<WebContentsImpl*>(shell()->web_contents());
FrameTreeNode* root = web_contents->GetFrameTree()->root();
FrameTreeNode* child_node = root->child_at(0);
EXPECT_NE(shell()->web_contents()->GetSiteInstance(),
child_node->current_frame_host()->GetSiteInstance());
WaitForHitTestData(child_node->current_frame_host());
RenderWidgetHostViewBase* root_view = static_cast<RenderWidgetHostViewBase*>(
root->current_frame_host()->GetRenderWidgetHost()->GetView());
RenderWidgetHostImpl* rwh_child =
root->child_at(0)->current_frame_host()->GetRenderWidgetHost();
RenderWidgetHostViewBase* child_view =
static_cast<RenderWidgetHostViewBase*>(rwh_child->GetView());
auto* router = web_contents->GetInputEventRouter();
RenderWidgetHostMouseEventMonitor child_monitor(
child_view->GetRenderWidgetHost());
RenderWidgetHostMouseEventMonitor root_monitor(
root_view->GetRenderWidgetHost());
// A cursor with enough room in the root view to fully display without
// blocking native UI should be shown.
{
blink::WebMouseEvent mouse_event(
blink::WebInputEvent::Type::kMouseMove,
blink::WebInputEvent::kNoModifiers,
blink::WebInputEvent::GetStaticTimeStampForTests());
SetWebEventPositions(&mouse_event, gfx::Point(300, 300), root_view);
auto set_cursor_interceptor =
std::make_unique<SetCursorInterceptor>(rwh_child);
RouteMouseEventAndWaitUntilDispatch(router, root_view, child_view,
&mouse_event);
set_cursor_interceptor->Wait();
EXPECT_TRUE(set_cursor_interceptor->cursor().has_value());
EXPECT_EQ(120, set_cursor_interceptor->cursor()->custom_bitmap().width());
EXPECT_EQ(120, set_cursor_interceptor->cursor()->custom_bitmap().height());
}
// A cursor without enough room to be fully enclosed within the root view
// should not be shown, even if the iframe is at an offset.
{
blink::WebMouseEvent mouse_event(
blink::WebInputEvent::Type::kMouseMove,
blink::WebInputEvent::kNoModifiers,
blink::WebInputEvent::GetStaticTimeStampForTests());
SetWebEventPositions(&mouse_event, gfx::Point(300, 115), root_view);
auto set_cursor_interceptor =
std::make_unique<SetCursorInterceptor>(rwh_child);
RouteMouseEventAndWaitUntilDispatch(router, root_view, child_view,
&mouse_event);
// We should see a new cursor come in that replaces the large one.
set_cursor_interceptor->Wait();
EXPECT_TRUE(set_cursor_interceptor->cursor().has_value());
EXPECT_NE(120, set_cursor_interceptor->cursor()->custom_bitmap().width());
EXPECT_NE(120, set_cursor_interceptor->cursor()->custom_bitmap().height());
}
}
#endif // !defined(OS_ANDROID) #endif // !defined(OS_ANDROID)
#if defined(USE_AURA) #if defined(USE_AURA)
......
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
height: 1000px;
width: 1000px;
cursor: -webkit-image-set(url(single_face.jpg) 1x) 120 120, auto;
}
</style>
...@@ -588,14 +588,27 @@ base::Optional<ui::Cursor> EventHandler::SelectCursor( ...@@ -588,14 +588,27 @@ base::Optional<ui::Cursor> EventHandler::SelectCursor(
continue; continue;
// For large cursors below the max size, limit their ability to cover UI // For large cursors below the max size, limit their ability to cover UI
// elements by removing them when they intersect with the visual viewport. // elements by removing them when they are not fully contained by the
// visual viewport. Careful, we need to make sure to translate coordinate
// spaces if we are in an OOPIF.
//
// TODO(csharrison): Consider sending a fallback cursor in the IPC to the // TODO(csharrison): Consider sending a fallback cursor in the IPC to the
// browser process so we can do that calculation there instead. // browser process so we can do that calculation there instead, this would
// ensure even a compromised renderer could not obscure browser UI with a
// large cursor. Also, consider augmenting the intervention to drop the
// cursor for iframes if the cursor image obscures content in the parent
// frame.
if (size.Width() > kMaximumCursorSizeWithoutFallback || if (size.Width() > kMaximumCursorSizeWithoutFallback ||
size.Height() > kMaximumCursorSizeWithoutFallback) { size.Height() > kMaximumCursorSizeWithoutFallback) {
IntRect cursor_rect(IntPoint(location.RoundedPoint() - hot_spot), size); PhysicalOffset cursor_offset =
IntRect visible_rect = page->GetVisualViewport().VisibleContentRect(); frame_->ContentLayoutObject()->LocalToAncestorPoint(
if (!visible_rect.Contains(cursor_rect)) { location.Point(),
nullptr, // no ancestor maps all the way up the hierarchy
kTraverseDocumentBoundaries | kApplyMainFrameTransform) -
PhysicalOffset(hot_spot);
PhysicalRect cursor_rect(cursor_offset, LayoutSize(size));
if (!PhysicalRect(page->GetVisualViewport().VisibleContentRect())
.Contains(cursor_rect)) {
Deprecation::CountDeprecation( Deprecation::CountDeprecation(
node->GetExecutionContext(), node->GetExecutionContext(),
WebFeature::kCustomCursorIntersectsViewport); WebFeature::kCustomCursorIntersectsViewport);
......
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