Gecko:Mouse Wheel Scrolling: Difference between revisions
Line 28: | Line 28: | ||
And there is another example, when the user is scrolling the page body, the sub-frame may come under the mouse cursor. Then, the sub-frame intercepts the mouse wheel events unexpectedly. | And there is another example, when the user is scrolling the page body, the sub-frame may come under the mouse cursor. Then, the sub-frame intercepts the mouse wheel events unexpectedly. | ||
We can fix these problems by a simple idea. That is, all users may want to scroll only one scrollable frame at one time. So, we can assume that two or more mouse wheel events which are fired between short term should scroll only one scrollable frame. The frame must be | We can fix these problems by a simple idea. That is, all users may want to scroll only one scrollable frame at one time. So, we can assume that two or more mouse wheel events which are fired between short term should scroll only one scrollable frame. The frame must be under the cursor at the first mouse wheel event. | ||
Gecko manages this transaction by nsMouseWheelTransaction which is in [http://mxr.mozilla.org/mozilla-central/source/content/events/src/nsEventStateManager.cpp nsEventStateManager.cpp]. This was implemented in [https://bugzilla.mozilla.org/show_bug.cgi?id=312831 bug 312831]. | Gecko manages this transaction by nsMouseWheelTransaction which is in [http://mxr.mozilla.org/mozilla-central/source/content/events/src/nsEventStateManager.cpp nsEventStateManager.cpp]. This was implemented in [https://bugzilla.mozilla.org/show_bug.cgi?id=312831 bug 312831]. |
Revision as of 15:57, 26 January 2010
Overview
Gecko can have multiple scrollable frames in its content. This documents the detail of mouse wheel scrolling rules in Gecko.
This first version is written for Gecko 1.9.2. But this document isn't stable, so, when this document will be updated for new behavior, the writer must write the version clearly.
Gecko honors the mouse cursor position
Basically, turning mouse wheel scrolls a scrollable view under the mouse cursor. This behavior was designed in bug 97283.
Note that on Windows, most mouse drivers honors keyboard focus rather than the mouse cursor position. However, Internet Explorer and other web browsers honor the cursor position too. So, it's not a problem, probably.
When there are two or more scrollable frames under the mouse cursor, only one frame should be scrolled. Gecko decides one scrollable frame for the target by following rules:
- Finds a frame under the mouse cursor.
- Checks whether the frame is scrollable to the direction by user operation.
- If the frame is scrollable, Gecko scrolls the frame.
- Otherwise, repeats the steps with its ancestor frames.
There is a special case. If gecko finds a drop down frame of a select element, it stops going up the frame hierarchy.
Mouse wheel transaction
By above rules, users can scroll one scrollable frame smoothly. However, when two or more scrollable frames are nested, we need additional rules.
For example, there is a page body which is scrollable and has a scrollable sub-frame (e.g., listbox of select element with multiple attribute, div element with overflow property, iframe element). When an user is scrolling down the sub-frame, it will be reached to the end of its content. However, some mouse wheel events which were fired after that will scroll the body unexpectedly.
And there is another example, when the user is scrolling the page body, the sub-frame may come under the mouse cursor. Then, the sub-frame intercepts the mouse wheel events unexpectedly.
We can fix these problems by a simple idea. That is, all users may want to scroll only one scrollable frame at one time. So, we can assume that two or more mouse wheel events which are fired between short term should scroll only one scrollable frame. The frame must be under the cursor at the first mouse wheel event.
Gecko manages this transaction by nsMouseWheelTransaction which is in nsEventStateManager.cpp. This was implemented in bug 312831.
The rules for end of a transaction:
- When no mouse wheel events are fired during specified term after latest mouse wheel event. The length is defined by mousewheel.transaction.timeout. The value means millisecond and the default value is 1500.
- When the mouse cursor moves to outside of the targeted scrollable frame.
- When the mouse cursor moves inside the targeted frame. However, we need to ignore some mouse move events which are fired unexpectedly when the user turned wheel. mousewheel.transaction.ignoremovedelay defines the time of ignoring mouse move events before and after a mouse wheel event. The value is millisecond, the default value is 100.
- When a keydown or keyup or keypress event is fired.
- When a mouse button down or mouse button up or click or double click event is fired.
- When a context menu key event or dragdrop_drop event is fired.
- When a target frame is destroyed.
Note that the transaction is updated only when a mouse wheel event scrolls the target frame actually. I.e., when an user keeps to turn mouse wheel to unscrollable direction, the transaction will be timed out. This is important for touchpad users, see bug 442774.
DOMMouseScroll event vs. Scrolling
A native mouse wheel event causes DOMMouseScroll events. The widget code dispatches an nsMouseScroll event when it handles a native mouse wheel event. Then, nsPresShell dispatches DOMMouseScroll events first. If preventDefault() wasn't called, nsEventStateManager tries to scroll a frame by the above rules.
So, the contents can prevent scrolling. At that time, mouse wheel transaction isn't updated.
Note that DOMMouseScroll events are fired by the actual cursor position. So, by the mouse wheel transaction system, the scroll target and the target of the DOMMouseScroll event may be different even if the target of DOMMouseScroll is scrollable.
Windows version of Gecko 1.9.2 and earlier doesn't fire DOMMouseScroll events when the mouse cursor is on a windowed plug-in and it consumes native mouse wheel events. This is going to be improved in bug 483136.
Acceleration system
See Firefox/Projects/AcceleratedScrolling for the detail.
This has many problems, so, this has been disabled in default settings.
Override system of system scroll speed
Instead of the acceleration, we provide an override mechanism of system scroll speed.
This is enabled in default settings only on Windows. mousewheel.system_scroll_override_on_root_content.enabled can switch it.
On Windows, If the system scroll speed settings are not customized by user or mouse driver, this system can override the scrolling speed. On others, this can override the speed every time.
The ratio can be specified by hidden prefs. mousewheel.system_scroll_override_on_root_content.vertical.factor is for vertical scrolling event. mousewheel.system_scroll_override_on_root_content.horizontal.factor is for horizontal scrolling event. The values are used as 1/100 (i.e., the default value 200 means 2.0).
nsEventStateManager multiplies the scrolling speed by the ratio.
See also bug 513817.
Hack for Thinkpad (Windows)
Issues
- We don't send mouse wheel events to windowless plug-ins (bug 359403).
- When an user is turning the mouse wheel but moving the mouse cursor, the transaction will not be finished even if the cursor is moved far.
- When an user finished scrolling a sub-frame but the sub-frame is clipped by its ancestor frame, the user doesn't still watch the all contents of the sub-frame (bug 428350).