Thoughts on APZ Keyboard Scrolling
To perform async keyboard scrolling, APZ would need to know two things:
(1) How to map keyboard events to scroll actions (e.g. "down arrow" to "scroll down by a line")
- The main thread could build a table containing this mapping, and send it over to APZ.
- Things to figure out:
- Does the mapping depend on the type of element in focus?
- Ehsan believes it doesn't.
- I don't understand this. If one has <textarea>, arrow down usually just moves caret, but may scroll too if caret is at the last visible line.
- Yeah, scrolling in <textarea>s is probably something APZ won't be able to do, because it would need to know how to move the caret as well, which would be quite difficult (it would need to know about character positions and such).
- Also can't scroll contenteditable elements or design mode
- Can the mapping change during runtime?
- If so, need to have the main thread send an update if it does.
(2) What to scroll (roughly, the nearest enclosing scrollframe of the currently focused element)
- Unlike other event types, there is no "position" associated with keyboard events that we could hit-test based on. It's based on focus.
- A possible design:
- Identify what types of events can potentially cause a focus change
- As a first pass, this could be "everything".
- Later we can refine it, possibly making it depend on the state of the page. For example, a mouse-move event can potentially cause a focus change *if* the page has registered an event listener for it.
- Have APZ assign a sequence number to all such events
- Note: this assumes all such events pass through APZ. If that's not the case, that might be a problem.
- APZ maintains 3 pieces of state:
- CurrentlyFocusedScrollId
- identifies the (nearest enclosing scrollframe of) the currently focused element
- SequenceNumberAtLastFocusChange
- the sequence number of the last event the main thread processed before last updating CurrentlyFocusedScrollId
- LastSequenceNumber
- the sequence number of the last event (of a type that may potentially cause a focus change) that APZ received
- I'd propose changing SequenceNumberAtLastFocusChange and LastSequenceNumber to LastContentProcessedEvent and LastAPZProcessedEvent.
- There's a possible complication here: Events can get forwarded to different content processes, or even not forwarded to any content process at all. Storing just one sequence number to express the information "All preceding events have been processed" may just be insufficient.
- Whenever the main thread processes a focus change, it sends an updated (CurrentlyFocusedScrollId, SequenceNumberAtLastFocusChange) pair to APZ
- It can also send a "null" value for CurrentlyFocusedScrollId, if the thing currently focused can't be scrolled by APZ (like a <textarea>)
- When APZ receives a keyboard event, it knows that CurrentlyFocusedScrollId is "up to date" as long as LastSequenceNumber == SequenceNumberAtLastFocusChange
- (Otherwise, an event that came before the keyboard event but hasn't been processed by the main thread yet might be about to cause a focus change.)
- If it's up to date, it consults the table from part (1), and scrolls the identified scroll frame.
- We can scroll only if the page doesn't have listeners for key events or doesn't call preventDefault
- Yep, good point. We have an existing mechanism for telling APZ whether the page has listeners for certain events, that we could extend for keyboard events.
- Otherwise, it falls back to having the main thread handle the keyboard event.
Action Items
- add telemetry for keyboard and mousemove listeners (uplift to beta if possible)
- propagate key events to APZ
- key -> action map in APZ
- from info in WidgetKeyboardEvent to "scroll by line" / "scroll by page" etc.
- note: might have multiple keys for a scroll action
- e.g. spacebar does page down
- listener detection and notification to APZ
- focus changes propagation to APZ
- testing / bug fxing
- mouse listener notification