Bug 96335 - Clicking a scrollbar unfocuses the current activeElement
Summary: Clicking a scrollbar unfocuses the current activeElement
Status: RESOLVED FIXED
Alias: None
Product: WebKit
Classification: Unclassified
Component: DOM (show other bugs)
Version: 528+ (Nightly build)
Hardware: All All
: P2 Normal
Assignee: Elliott Sprehn
URL:
Keywords: HasReduction
Depends on:
Blocks:
 
Reported: 2012-09-10 16:25 PDT by Elliott Sprehn
Modified: 2022-04-24 13:56 PDT (History)
11 users (show)

See Also:


Attachments
Reduction (532 bytes, text/html)
2012-09-10 16:25 PDT, Elliott Sprehn
no flags Details
Patch (13.52 KB, patch)
2012-09-10 21:59 PDT, Elliott Sprehn
no flags Details | Formatted Diff | Diff
Patch (13.51 KB, patch)
2012-09-11 14:20 PDT, Elliott Sprehn
no flags Details | Formatted Diff | Diff
Patch (10.81 KB, patch)
2012-09-18 11:44 PDT, Elliott Sprehn
no flags Details | Formatted Diff | Diff
Patch (10.75 KB, patch)
2012-09-19 15:19 PDT, Elliott Sprehn
webkit.review.bot: commit-queue-
Details | Formatted Diff | Diff
Patch (10.85 KB, patch)
2012-09-19 17:44 PDT, Elliott Sprehn
no flags Details | Formatted Diff | Diff
Patch (11.46 KB, patch)
2012-12-03 13:57 PST, Elliott Sprehn
no flags Details | Formatted Diff | Diff
Patch for landing (11.56 KB, patch)
2012-12-03 18:22 PST, Elliott Sprehn
no flags Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Elliott Sprehn 2012-09-10 16:25:32 PDT
Created attachment 163242 [details]
Reduction

If you activate a textbox and then scroll a div the textbox loses focus. There's special code in EventHandler for the frame's scrollbar to not move the focus, but overflow scrollbars are not handled properly.
Comment 1 Elliott Sprehn 2012-09-10 16:26:44 PDT
Note: There's a catch here. We *should* move the focus when clicking the scrollbar in a <textarea>, but not for other elements.
Comment 2 Elliott Sprehn 2012-09-10 21:31:11 PDT
(In reply to comment #1)
> Note: There's a catch here. We *should* move the focus when clicking the scrollbar in a <textarea>, but not for other elements.

This is actually true for all form controls, not just textarea. ex. Clicking the scrollbar in a multi select.
Comment 3 Elliott Sprehn 2012-09-10 21:59:23 PDT
Created attachment 163277 [details]
Patch

Don't move focus when clicking scrollbars unless inside an enabled form control.
Comment 4 Elliott Sprehn 2012-09-11 14:20:26 PDT
Created attachment 163435 [details]
Patch

Pass the IntPoint instead of the whole PlatformMouseEvent.
Comment 5 Antonio Gomes 2012-09-13 08:16:23 PDT
Comment on attachment 163435 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=163435&action=review

> Source/WebCore/ChangeLog:13
> +        This patch also simplifies handleMousePressEvent and removes some dead code related to
> +        widget based input controls that no longer exist.

can the removal be on its own patch?
Comment 6 Ryosuke Niwa 2012-09-13 15:24:15 PDT
Comment on attachment 163435 [details]
Patch

Yeah, this patch does too much. We need to separate the two changes. Historically, this code is very under-tested so making a big change like this is likely to cause a regression.
Comment 7 Elliott Sprehn 2012-09-18 11:44:30 PDT
Created attachment 164596 [details]
Patch

Remove all the cleanup code. Only fix the bug.
Comment 8 Antonio Gomes 2012-09-19 08:55:26 PDT
Comment on attachment 164596 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=164596&action=review

Implementation looks good. Most important thing now is to decide that is the behavior we want. Could you add to your changelog other browsers' behavior we now match/dismatch?

One question below:

> Source/WebCore/page/EventHandler.cpp:2298
> +    if (FrameView* view = m_frame->view()) {
> +        if (view->scrollbarAtPoint(windowPoint))
> +            return true;
> +    }
> +
> +    if (RenderView* renderView = m_frame->contentRenderer()) {
> +        HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active);
> +        HitTestResult result(windowPoint);
> +        renderView->hitTest(request, result);
> +        return result.scrollbar();
> +    }
> +
> +    return false;

Do we need both ifs? when would one fail and the other not?
Comment 9 Daniel Bates 2012-09-19 13:01:29 PDT
Comment on attachment 164596 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=164596&action=review

> Source/WebCore/page/EventHandler.cpp:2263
> +        // Don't change the focus if the click is inside a scrollbar unless it's in a form control.

Interpreting a sentence that contains a negation tends to be more difficult than interpreting a sentence that is written in a positive form (i.e. doesn't contain with a negation). This is exacerbated when such a sentence contains more than one negation, especially a "unless" clause. See my remark below for one idea how we can encode the content of this comment into a more readable for by taking advantage of the flow control structure (below) to provide context for an inline comment.

> Source/WebCore/page/EventHandler.cpp:2266
> +        Element* element = node && node->isElementNode() ? toElement(node) : 0;
> +        if (!(element && element->isFormControlElement()) && isInsideScrollbar(mouseEvent.position()))
> +            return true;

This code is OK as-is. We can simplify this code, noticing that element is null when either node is null or node->isElementNode() evaluates to false, to read:

if (node && node->isElementNode()) {
    if (!toElement(node)->isFormControlElement() && isInsideScrollbar(mouseEvent.position()))
        return true; // Don't change focus.
}
Comment 10 Elliott Sprehn 2012-09-19 13:47:27 PDT
(In reply to comment #8)
> (From update of attachment 164596 [details])
> View in context: https://bugs.webkit.org/attachment.cgi?id=164596&action=review
> 
> ... 
> Do we need both ifs? when would one fail and the other not?

I'm not sure if it's possible. If you look at FrameLoader::open we can create a Frame and set a FrameView, but it's not clear that the document will be attached immediately, or if you could have a lazy attach on a loading frame.

It also looks like anything that goes through FrameLoader::clear will have document set to 0, removing the RenderView, but it would still have a FrameView.
Comment 11 Elliott Sprehn 2012-09-19 14:52:18 PDT
Downstream Chrome bug: http://code.google.com/p/chromium/issues/detail?id=51469

After testing it appears that we match IE9 and Opera with our current behavior, and it's Gecko that doesn't move the focus. Hmm... so which one should we match?
Comment 12 Adam Barth 2012-09-19 14:58:51 PDT
> After testing it appears that we match IE9 and Opera with our current behavior, and it's Gecko that doesn't move the focus. Hmm... so which one should we match?

It might be worth talking with folks in #developers on irc.mozilla.org to see if they're interested in changing.  If Gecko changed, then all the browsers would work the same way, right?
Comment 13 Elliott Sprehn 2012-09-19 15:13:15 PDT
(In reply to comment #12)
> > After testing it appears that we match IE9 and Opera with our current behavior, and it's Gecko that doesn't move the focus. Hmm... so which one should we match?
> 
> It might be worth talking with folks in #developers on irc.mozilla.org to see if they're interested in changing.  If Gecko changed, then all the browsers would work the same way, right?

The original bug referenced this stackoverflow post that mentions IE8, Firefox and Safari didn't have this behavior, but I just tested IE8/WinXP and IE9/Win7 and it does move the focus:

http://stackoverflow.com/questions/1345473/google-chrome-focus-issue-with-the-scrollbar

Firefox's behavior of not moving the focus seems more logical to me. Just because I scrolled down a form doesn't mean I wanted to unfocus the input I was typing into. It's also weird that the FrameView's scrollbars are special, it means you don't get the same behavior from a full height div with an overflow scrollbar (ex. Google+).
Comment 14 Elliott Sprehn 2012-09-19 15:19:13 PDT
Created attachment 164784 [details]
Patch

Review comment changes.
Comment 15 WebKit Review Bot 2012-09-19 16:34:40 PDT
Comment on attachment 164784 [details]
Patch

Attachment 164784 [details] did not pass chromium-ews (chromium-xvfb):
Output: http://queues.webkit.org/results/13903656

New failing tests:
scrollbars/scrollbar-iframe-click-does-not-blur-content.html
scrollbars/scrollbar-click-does-not-blur-content.html
fast/events/mousedown-in-subframe-scrollbar.html
fast/overflow/scrollbar-click-retains-focus.html
Comment 16 Elliott Sprehn 2012-09-19 17:10:35 PDT
Comment on attachment 164596 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=164596&action=review

>> Source/WebCore/page/EventHandler.cpp:2266
>> +            return true;
> 
> This code is OK as-is. We can simplify this code, noticing that element is null when either node is null or node->isElementNode() evaluates to false, to read:
> 
> if (node && node->isElementNode()) {
>     if (!toElement(node)->isFormControlElement() && isInsideScrollbar(mouseEvent.position()))
>         return true; // Don't change focus.
> }

Your suggested code is not correct and is not logically equivalent to the original code. It now fails for cases where there is no node, but the click is inside a scrollbar (ie. it's the FrameView's scrollbar).
Comment 17 Elliott Sprehn 2012-09-19 17:44:49 PDT
Created attachment 164807 [details]
Patch
Comment 18 Daniel Bates 2012-09-19 18:19:46 PDT
(In reply to comment #16)
> [...]
> Your suggested code is not correct and is not logically equivalent to the original code. It now fails for cases where there is no node, but the click is inside a scrollbar (ie. it's the FrameView's scrollbar).

You're right. Disregard my suggestion.
Comment 19 Ojan Vafai 2012-09-19 18:24:18 PDT
Comment on attachment 164807 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=164807&action=review

> Source/WebCore/page/EventHandler.cpp:2265
> +        if ((!element || !element->isFormControlElement()) && isInsideScrollbar(mouseEvent.position()))

We probably also want to move focus for contentEditable elements?
Comment 20 Ojan Vafai 2012-09-19 18:33:35 PDT
The OS X, Windows and Ubuntu native UIs don't move focus when you click on a scrollbar and WebKit doesn't move focus if you click on the viewport scrollbar. So, it seems like the most consistent thing is to not move focus when clicking on scrollbars.

It's not clear to me if we should be moving focus when you click on a textarea/contentEditable scrollbar. Seems more consistent to not do so. I'm not sure if there's a native platform convention here. Would be nice to get consensus on this in a spec somewhere. HTML talks about activeElements. Maybe WHATWG?
Comment 21 Ojan Vafai 2012-10-31 16:10:42 PDT
Comment on attachment 164807 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=164807&action=review

>> Source/WebCore/page/EventHandler.cpp:2265
>> +        if ((!element || !element->isFormControlElement()) && isInsideScrollbar(mouseEvent.position()))
> 
> We probably also want to move focus for contentEditable elements?

Firefox actually looks at whether the element is mouseFocusable. So, if we're going to have a check here, it should be that.
Comment 22 Elliott Sprehn 2012-12-03 13:57:18 PST
Created attachment 177323 [details]
Patch
Comment 23 Elliott Sprehn 2012-12-03 13:57:49 PST
(In reply to comment #22)
> Created an attachment (id=177323) [details]
> Patch

Updated to now check isMouseFocusable and made the test cover these cases.
Comment 24 Ojan Vafai 2012-12-03 16:32:44 PST
Comment on attachment 177323 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=177323&action=review

Please make sure to address the non-nits before committing.

> Source/WebCore/page/EventHandler.cpp:2398
> +        if ((!node || !node->isMouseFocusable()) && isInsideScrollbar(mouseEvent.position()))

Looks like you can remove the null checks in the code below now that you're null-checking node here.

> Source/WebCore/page/EventHandler.cpp:2420
> +        HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active);

I don't think you need HitTestRequest::Active here. I know a bunch of places pass both ReadOnly and Active, but looking at the code, I expect they're all wrong. Not sure. Did you find something broke without the Active?

> LayoutTests/fast/overflow/scrollbar-click-retains-focus.html:50
> +function click(x, y) {

Nit: curly should go on new line. The de-facto style for JS is that we put the curly on new lines for names functions, but not anonymous ones.

> LayoutTests/fast/overflow/scrollbar-click-retains-focus.html:79
> +    shouldBeEqualToString("document.activeElement.tagName", "INPUT");

Won't these fail on platforms that use overlay scrollbars or do overlay scrollbars always show up for overflow:scroll? I'm not sure if there's a way to test overlay scrollbars at all. You could make the test work by measuring the width of the scrollbar (subtract clientWidth from offsetWidth on an overflow:scroll element). Or you could WONTFIX it for platforms with overlay scrollbars.
Comment 25 Elliott Sprehn 2012-12-03 16:56:45 PST
Comment on attachment 177323 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=177323&action=review

>> Source/WebCore/page/EventHandler.cpp:2398
>> +        if ((!node || !node->isMouseFocusable()) && isInsideScrollbar(mouseEvent.position()))
> 
> Looks like you can remove the null checks in the code below now that you're null-checking node here.

You can't because this null check only counts if the click is inside a scrollbar, notice the parens.

>> Source/WebCore/page/EventHandler.cpp:2420
>> +        HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active);
> 
> I don't think you need HitTestRequest::Active here. I know a bunch of places pass both ReadOnly and Active, but looking at the code, I expect they're all wrong. Not sure. Did you find something broke without the Active?

Oh wow, it seems the code has changed quite a big since I originally uploaded this patch. At one point you needed to pass both.

>> LayoutTests/fast/overflow/scrollbar-click-retains-focus.html:79
>> +    shouldBeEqualToString("document.activeElement.tagName", "INPUT");
> 
> Won't these fail on platforms that use overlay scrollbars or do overlay scrollbars always show up for overflow:scroll? I'm not sure if there's a way to test overlay scrollbars at all. You could make the test work by measuring the width of the scrollbar (subtract clientWidth from offsetWidth on an overflow:scroll element). Or you could WONTFIX it for platforms with overlay scrollbars.

DRT always uses regular scrollbars unless you do setUsesOverlayScrollbars so that shouldn't be the case.
Comment 26 Elliott Sprehn 2012-12-03 18:22:07 PST
Created attachment 177389 [details]
Patch for landing
Comment 27 WebKit Review Bot 2012-12-04 22:24:10 PST
Comment on attachment 177389 [details]
Patch for landing

Clearing flags on attachment: 177389

Committed r136642: <http://trac.webkit.org/changeset/136642>
Comment 28 WebKit Review Bot 2012-12-04 22:24:16 PST
All reviewed patches have been landed.  Closing bug.