Commit ffb0853a authored by raymes@chromium.org's avatar raymes@chromium.org

Fix event passing to overlay scrollbars when over a plugin

There are two fixes here:
1) Previously frame scrollbars never had a chance to handle an event if an element or event handler swallowed the event. So, for example, if a mousedown event listener was added to the window and the handler called preventDefault(), then the frame scrollbar would never receive mouse clicks. This change always gives frame scrollbars an opportunity to handle the event. This would also affect elements (such as plugins) that lie underneath overlay scrollbars and swallowed events.

2) Previously if an overlay scrollbar was above a plugin, clicking on the scrollbar could trigger mouse capture on the plugin, which would interfere with the scrollbar receiving events. This change prevents mouse capture on a plugin from starting if the event was initiated above a scrollbar.

BUG=369898,358248

Review URL: https://codereview.chromium.org/296003011

git-svn-id: svn://svn.chromium.org/blink/trunk@175857 bbb929c8-8fbe-4397-9dbb-9b2b20218538
parent 69e8054e
......@@ -486,6 +486,7 @@ crbug.com/265650 virtual/softwarecompositing/repaint/transform-style-change.html
crbug.com/266754 fast/ruby/ruby-svg-crash.html [ ImageOnlyFailure Pass ]
crbug.com/267206 [ Lion MountainLion Retina ] fast/scrolling/scrollbar-tickmarks-hittest.html [ Timeout ]
crbug.com/267206 [ Mac ] plugins/overlay-scrollbar-mouse-capture.html [ NeedsRebaseline ]
crbug.com/267302 [ Win ] http/tests/htmlimports/import-script-block-crossorigin-dynamic.html [ Pass Failure ]
......
This tests that scrollbars always receive events even when there is an element underneath the scrollbar which swallows the event. In this case an event handler is added to the window which swallows the event but this may also happen with elements underneath of overlay scrollbars.
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
PASS window.scrollY is 0
PASS receivedMousedownEvent is true
PASS subframe.scrollTop is 0
PASS receivedMousedownEvent is true
PASS successfullyParsed is true
TEST COMPLETE
<head>
<script src="../../resources/js-test.js"></script>
<style>
body {
height: 200vh;
width: 200vw;
margin: 0;
}
#subframe {
width: 100px;
height: 100px;
background-color: black;
overflow: scroll;
}
#subframe-content {
width: 200px;
height: 200px;
background-color: black;
}
</style>
</head>
<body>
<div id="subframe">
<div id="subframe-content"></div>
</div>
<pre id="console"></pre>
</body>
<script>
description('This tests that scrollbars always receive events even when there is an element underneath the scrollbar which swallows the event. In this case an event handler is added to the window which swallows the event but this may also happen with elements underneath of overlay scrollbars.');
if (window.testRunner)
testRunner.dumpAsText();
if (window.internals)
internals.settings.setMockScrollbarsEnabled(true)
var receivedMousedownEvent = false;
document.addEventListener('mousedown', function(e) {
e.preventDefault();
receivedMousedownEvent = true;
});
// Test the frame scrollbar.
window.scrollTo(0, 10);
if (window.eventSender) {
eventSender.mouseMoveTo(window.innerWidth - 5, 1);
eventSender.mouseDown();
eventSender.mouseUp();
}
shouldBe('window.scrollY', '0');
shouldBe('receivedMousedownEvent', 'true');
// Test a div scrollbar.
receivedMousedownEvent = false;
var subframe = document.getElementById('subframe');
subframe.scrollTop = 10;
if (window.eventSender) {
eventSender.mouseMoveTo(100 - 5, 1);
eventSender.mouseDown();
eventSender.mouseUp();
}
shouldBe('subframe.scrollTop', '0');
shouldBe('receivedMousedownEvent', 'true');
</script>
This tests whether scrolling still works correctly when an overlay scrollbar is over a plugin. The plugin should still receive mouse down/up events when clicking an overlay scrollbar. Scrolling should still work correctly too. However mouse capture should not be started on the plugin as this would interfere with events going to the scrollbar.
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
PASS eventHistory.length is 2
PASS eventHistory[0] is "plugin.mousedown"
PASS eventHistory[1] is "plugin.mouseup"
PASS window.scrollY is not 0
PASS successfullyParsed is true
TEST COMPLETE
<head>
<script src="../resources/js-test.js"></script>
<style>
body {
/* Hide the horizontal-scrollbar so that clicking right at the
bottom of the vertical scrollbar will trigger a scroll */
overflow-x: hidden;
}
#container {
/* The plugin is guaranteed not to be in the margin. */
margin-left: 10px;
}
</style>
</head>
<body>
<div id="container"></div>
<pre id="console"></pre>
</body>
<script>
description('This tests whether scrolling still works correctly when an overlay scrollbar is over a plugin. The plugin should still receive mouse down/up events when clicking an overlay scrollbar. Scrolling should still work correctly too. However mouse capture should not be started on the plugin as this would interfere with events going to the scrollbar.');
if (window.testRunner)
testRunner.dumpAsText();
if (window.internals)
internals.settings.setOverlayScrollbarsEnabled(true);
var startLogging = false;
var eventHistory = [];
var d = document.getElementById('container');
var plugin = document.createElement('object');
plugin.type = 'application/x-webkit-test-netscape';
plugin.width = window.innerWidth * 2;
plugin.height = window.innerHeight * 2;
plugin.addEventListener('mousedown', function(e) {
startLogging = true;
eventHistory.push('plugin.mousedown');
});
plugin.addEventListener('mouseup', function(e) {
if (startLogging)
eventHistory.push('plugin.mouseup');
});
plugin.addEventListener('mousemove', function(e) {
if (startLogging)
eventHistory.push('plugin.mousemove');
});
d.appendChild(plugin);
if (window.eventSender) {
// Mouse down on the scrollbar which is over the plugin.
eventSender.mouseMoveTo(window.innerWidth - 1,
window.innerHeight - 1);
eventSender.mouseDown();
// Move outside the plugin, it shouldn't receive any events
// because there shouldn't be any mouse capture.
eventSender.mouseMoveTo(5, 5);
// A mouse up will be received because when dragging off a
// scrollbar and releasing, it dispatches an event to the last
// element under the mouse.
eventSender.mouseUp();
}
shouldBe('eventHistory.length', '2');
shouldBe('eventHistory[0]', '"plugin.mousedown"');
shouldBe('eventHistory[1]', '"plugin.mouseup"');
shouldNotBe('window.scrollY', '0');
</script>
......@@ -1307,26 +1307,14 @@ bool EventHandler::handleMousePressEvent(const PlatformMouseEvent& mouseEvent)
if (swallowEvent) {
// scrollbars should get events anyway, even disabled controls might be scrollable
Scrollbar* scrollbar = mev.scrollbar();
updateLastScrollbarUnderMouse(scrollbar, true);
if (scrollbar)
passMousePressEventToScrollbar(mev, scrollbar);
passMousePressEventToScrollbar(mev);
} else {
if (shouldRefetchEventTarget(mev)) {
HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::ConfusingAndOftenMisusedDisallowShadowContent);
mev = m_frame->document()->prepareMouseEvent(request, documentPoint, mouseEvent);
}
FrameView* view = m_frame->view();
Scrollbar* scrollbar = view ? view->scrollbarAtPoint(mouseEvent.position()) : 0;
if (!scrollbar)
scrollbar = mev.scrollbar();
updateLastScrollbarUnderMouse(scrollbar, true);
if (scrollbar && passMousePressEventToScrollbar(mev, scrollbar))
if (passMousePressEventToScrollbar(mev))
swallowEvent = true;
else
swallowEvent = handleMousePressEvent(mev);
......@@ -3338,8 +3326,18 @@ void EventHandler::setFrameWasScrolledByUser()
view->setWasScrolledByUser(true);
}
bool EventHandler::passMousePressEventToScrollbar(MouseEventWithHitTestResults& mev, Scrollbar* scrollbar)
bool EventHandler::passMousePressEventToScrollbar(MouseEventWithHitTestResults& mev)
{
// First try to use the frame scrollbar.
FrameView* view = m_frame->view();
Scrollbar* scrollbar = view ? view->scrollbarAtPoint(mev.event().position()) : 0;
// Then try the scrollbar in the hit test.
if (!scrollbar)
scrollbar = mev.scrollbar();
updateLastScrollbarUnderMouse(scrollbar, true);
if (!scrollbar || !scrollbar->enabled())
return false;
setFrameWasScrolledByUser();
......
......@@ -274,7 +274,7 @@ private:
bool passMouseMoveEventToSubframe(MouseEventWithHitTestResults&, LocalFrame* subframe, HitTestResult* hoveredNode = 0);
bool passMouseReleaseEventToSubframe(MouseEventWithHitTestResults&, LocalFrame* subframe);
bool passMousePressEventToScrollbar(MouseEventWithHitTestResults&, Scrollbar*);
bool passMousePressEventToScrollbar(MouseEventWithHitTestResults&);
bool passWidgetMouseDownEventToWidget(const MouseEventWithHitTestResults&);
......
......@@ -480,14 +480,16 @@ void WebViewImpl::handleMouseDown(LocalFrame& mainFrame, const WebMouseEvent& ev
m_lastMouseDownPoint = WebPoint(event.x, event.y);
if (event.button == WebMouseEvent::ButtonLeft) {
IntPoint point(event.x, event.y);
// Take capture on a mouse down on a plugin so we can send it mouse events.
// If the hit node is a plugin but a scrollbar is over it don't start mouse
// capture because it will interfere with the scrollbar receiving events.
IntPoint point(event.x, event.y);
if (event.button == WebMouseEvent::ButtonLeft && !m_page->mainFrame()->view()->scrollbarAtPoint(point)) {
point = m_page->mainFrame()->view()->windowToContents(point);
HitTestResult result(m_page->mainFrame()->eventHandler().hitTestResultAtPoint(point));
Node* hitNode = result.innerNonSharedNode();
// Take capture on a mouse down on a plugin so we can send it mouse events.
if (hitNode && hitNode->renderer() && hitNode->renderer()->isEmbeddedObject()) {
if (!result.scrollbar() && hitNode && hitNode->renderer() && hitNode->renderer()->isEmbeddedObject()) {
m_mouseCaptureNode = hitNode;
TRACE_EVENT_ASYNC_BEGIN0("input", "capturing mouse", this);
}
......
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