Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.

Bug 370548

Summary: Text selection flickers / goes away when the mouse pointer goes outside of the view
Product: [ECD] Orion Reporter: Mihai Sucan <mihai.sucan>
Component: EditorAssignee: Silenio Quarti <Silenio_Quarti>
Status: RESOLVED FIXED QA Contact:
Severity: normal    
Priority: P2 CC: eclipse.felipe, mihai.sucan, Silenio_Quarti
Version: unspecified   
Target Milestone: 0.4 RC1   
Hardware: All   
OS: All   
Whiteboard:

Description Mihai Sucan CLA 2012-02-03 07:59:15 EST
In Firefox, with the most recent Orion integration and with the upcoming Orion integration code from last week, we have a weird selection flicker that happens while holding down the left mouse button and selecting text, going outside of the editor view.

This seems to only happen in the integration of Orion, even if we have it unpatched. On the web I cannot reproduce the bug.

STR:

1. Start a Firefox nightly and open Scratchpad (Shift-F4).
2. Paste some code with long lines.
3. Select text and move the mouse to the left, onto the line numbers ruler and further beyond. Notice how the selection animates wrongly or it flickers. It doesn't settle down.

I have identified the code with the problem and I am going to submit a proposed patch.
Comment 1 Mihai Sucan CLA 2012-02-03 08:11:48 EST
Forgot to mention this is also a P1 bug in our Bugzilla:

https://bugzilla.mozilla.org/show_bug.cgi?id=717631


Here's the proposed patch:

https://github.com/mihaisucan/orion.client/commit/69f5b8422f36d564a3a1010f6ac64d0376ce0a27

Problem explanation:  the autoScroll function, based on a timer, keeps decrementing/incrementing the x offset of the desired caret location, going towards the left. This achieves the left/right scrolling when needed and it moves the caret and the selection is extended properly. However, when the mouse goes outside the view, outside of the left edge the desired caret location is never found (of course, since it's an X offset outside of the view). Nonetheless, getXToOffset() is called with an offset that goes outside the view. There's code to cope with that (if x < lineRect.left), but there's a bug in the while (lineChild) loop. If the desired x offset is never found withing the line children rects offset is still incremented with the nodeLength, even if the x offset is far on the left. So, instead of keeping offset = lineStart ... the offset is incremented each time and the end effect is that offset becomes lineEnd. This is why the selection keeps animating/flickering because it loops between the start and end line offsets, on the autoscroll timer function.

I added a check to the code in the while loop to avoid incrementing offset if it's not needed. This seems to work for me. Please review - I might have missed something! Thank you!
Comment 2 Felipe Heidrich CLA 2012-02-06 11:21:33 EST
The code clamps the x value to always be within the bounding rect of the parent (child). The fact that the 'while loop' is not finding a child that contains the x location suggests there is an inconsistency between
child.getBoundingClientRect() and lineChild.getClientRects()



Note that the method was coded this way (always checking for inclusion:rect.left <= x && x < rect.right) to support bidi reordering. The patch you sent will probably break for bidi.
Comment 3 Felipe Heidrich CLA 2012-02-06 13:52:56 EST
is there any padding in the child element (the line div) ?
Comment 4 Felipe Heidrich CLA 2012-02-06 14:15:45 EST
Mihai, test if this code fixes the problem:

Replace the current implementation of _getLineBoundingClientRect() with:
_getLineBoundingClientRect: function (child) {
	var rect = child.getBoundingClientRect();
	rect = {left: Number.MAX_VALUE, top: rect.top, right: Number.MIN_VALUE, bottom: rect.bottom};
	var lineChild = child.firstChild, childRect;
	while (lineChild) {
		if (lineChild.ignoreChars !== lineChild.firstChild.length) {
			childRect = lineChild.getBoundingClientRect();
			if (childRect.left < rect.left) { rect.left = childRect.left; }
			if (childRect.right > rect.right) { rect.right = childRect.right; }
		}
		lineChild = lineChild.nextSibling;
	}
	return rect;
}

-
This code probably has bad performance, but it should help us to understand the problem.
Comment 5 Mihai Sucan CLA 2012-02-07 08:40:36 EST
(In reply to comment #4)
> Mihai, test if this code fixes the problem:
> 
> Replace the current implementation of _getLineBoundingClientRect() with:
> _getLineBoundingClientRect: function (child) {
> 
> [...]
> This code probably has bad performance, but it should help us to understand the
> problem.

Tested. It works!

I remember I also had a problem with this method when I was fixing / looking into some older bug. I am not sure what I did back then, but IIRC I did avoid making changes to this very low level method.

Do you have any ideas on how we can get this fixed?
Comment 6 Silenio Quarti CLA 2012-02-07 13:18:32 EST
What is the difference between the rect returned by the hacked version of _getLineBoundingClientRect() and the committed one? Is it more than one pixel?
Comment 7 Mihai Sucan CLA 2012-02-07 13:36:26 EST
(In reply to comment #6)
> What is the difference between the rect returned by the hacked version of
> _getLineBoundingClientRect() and the committed one? Is it more than one pixel?

Yes, more than one pixel:

_getLineBoundingClientRect_old l 25 t 103 r 573 b 120
_getLineBoundingClientRect l 29 t 103 r 573 b 120

When I select and move the mouse where there are no lines (eg. if I have only 3 lines in the document and I move the mouse towards the bottom of the view), I get:

_getLineBoundingClientRect_old l 25 t 154 r 25 b 171
_getLineBoundingClientRect l 1.7976931348623157e+308 t 154 r 5e-324 b 171

Selection rendering changed a bit:

http://img.i7m.de/show/u0hik.png

Notice a few missing pixels at the start of the selection. Also notice the same number of selected pixels at the end of the selection.
Comment 8 Felipe Heidrich CLA 2012-02-07 16:05:33 EST
(In reply to comment #7)

That huge difference that you see in an empty line is just bug in the new code...

Now, that 4 pixels looks like padding in the line div. Would you be able to verify is there is any padding in the element (using firebug for example) ?

If you do wish to have padding around the content use the .view class in the css file.
Comment 9 Mihai Sucan CLA 2012-02-07 16:25:45 EST
(In reply to comment #8)
> (In reply to comment #7)
> 
> That huge difference that you see in an empty line is just bug in the new
> code...
> 
> Now, that 4 pixels looks like padding in the line div. Would you be able to
> verify is there is any padding in the element (using firebug for example) ?
> 
> If you do wish to have padding around the content use the .view class in the
> css file.

Checked. Yes, that is correct. If I remove the padding I have... the selection no longer flickers and this change fixes this bug. Thank you!

Now the reason we did add padding to each line was because the selection is rendered a few pixels to the right from the line gutter, which looks weird. What do you suggest we do?
Comment 10 Felipe Heidrich CLA 2012-02-07 16:36:22 EST
> Now the reason we did add padding to each line was because the selection is
> rendered a few pixels to the right from the line gutter, which looks weird.
> What do you suggest we do?

The only element the code is ready to handle padding is the viewDiv, define a  "view" class specifying the desired padding in your css file that is passed to the constructor of the TextView. 


See TextView#_getFrameHTML() (to see the default values) and org.eclipse.orion.client.editor/web/orion/textview/textview.css (currently it only sets the background to white).
Comment 11 Mihai Sucan CLA 2012-02-07 16:39:31 EST
(In reply to comment #10)
> > Now the reason we did add padding to each line was because the selection is
> > rendered a few pixels to the right from the line gutter, which looks weird.
> > What do you suggest we do?
> 
> The only element the code is ready to handle padding is the viewDiv, define a 
> "view" class specifying the desired padding in your css file that is passed to
> the constructor of the TextView. 

I did that, .view { paddling-left: 4px }. But this pushes the text selection as well. This is why I was asking...
Comment 12 Felipe Heidrich CLA 2012-02-08 09:44:19 EST
(In reply to comment #11)

> I did that, .view { paddling-left: 4px }. But this pushes the text selection as
> well. This is why I was asking...

I'm sorry, I missed that.

It seems that we would have to fix our hit test to respect padding in the lineDivs?

Silenio, do you see an alternative ?
Comment 13 Felipe Heidrich CLA 2012-02-08 16:09:47 EST
http://git.eclipse.org/c/orion/org.eclipse.orion.client.git/commit/?id=87adb02b69e2ba6f68942fd3326927378d086b31

Mihai, try this out. We are trying to support padding and border in the lineDiv element. Let me know if it works for you. Thanks.
Comment 14 Mihai Sucan CLA 2012-02-08 16:28:01 EST
(In reply to comment #13)
> http://git.eclipse.org/c/orion/org.eclipse.orion.client.git/commit/?id=87adb02b69e2ba6f68942fd3326927378d086b31
> 
> Mihai, try this out. We are trying to support padding and border in the lineDiv
> element. Let me know if it works for you. Thanks.

How risky is the code change? I'd prefer to use padding-left on .view rather than take a patch now that might cause other regressions. I'm "late" in my process of updating the source editor for Firefox and I'd like to cherry-pick only safe changes now. If you consider it's "risky" then I'll take this later, in a future Orion update.


Thank you very much for your work - Silenio and Felipe!