We have support for using (shift+)tab to move focus to the next/previous
element on the page. However, there were several ways for this to crash
as written. This updates our implementation to check if we did not find
a node to move focus to, and to reset focus to the first/last node in
the document.
This doesn't seem to work when wrapping around from the first to the
last node. A FIXME has been added for that, as this would already not
work before this patch (the main focus here is not crashing).
Our handling of left vs. right modifiers keys (shift, ctrl, etc.) was
largely not to spec. This patch adds explicit UIEvents::KeyCode values
for these keys, and updates the UI to match native key events to these
keys (as best as we are able).
We were generating click events always using the primary mouse button
instead of the provided button, and with the buttons field set to that
provided button.
The expensive part of creating a segmenter is doing the locale and UCD
data lookups at creation time. Instead of doing this once per text node,
cache the segmenters on the document, and clone them as needed (cloning
is much, much cheaper).
On a profile loading Ladybird's GitHub repo, the following hot methods
changed as follows:
ChunkIterator ctor: 6.08% -> 0.21%
Segmenter factory: 5.86% -> 0%
Segmenter clone: N/A -> 0.09%
UI event handlers currently return a boolean where false means the event
was cancelled by a script on the page, or otherwise dropped. It has been
a point of confusion for some time now, as it's not particularly clear
what should be returned in some special cases, or how the UI process
should handle the response.
This adds an enumeration with a few states that indicate exactly how the
WebContent process handled the event. This should remove all ambiguity,
and let us properly handle these states going forward.
There should be no behavior change with this patch. It's meant to only
introduce the enum, not change any of our decisions based on the result.
This permits the user to use shift and the arrow/home/end keys to mutate
the document selection. Arrow key presses on non-editable text without
the shift key held has no effect.
This behavior differs browser-to-browser. The behavior here most closely
matches Firefox, though all browsers support this to some degree.
When an editable node is focused and one of the arrow/home/end keys are
pressed while shift is held, we will now create or update the document's
selection. There is a bit of nuance to the behavior here, which matches
how the cursor behaves in other engines.
We will of course want to abstract this in the future to extend any non-
editable node text selections. This also does not implement holding ctrl
to jump by word, rather than grapheme.
If a key is pressed when the media player is in focus, which causes the
media player to perform some action, that key event is no longer
propagated further.
This causes UI interactions with the document selection to also update
the input and textarea DOM selection state. Note that we switch around
the order of focusing a DOM node and setting the selection, so we allow
the focus event to override whatever selection we came up with.
Previously, only DOM nodes with `is_editable()` allowed selection via
the mouse. This had the unwanted consequence, that read-only
input/textarea elements did not allow selection.
Now, `EventHandler::handle_mousedown()` asks the node's non-shadow
parent element over the new virtual method `is_child_node_selectable()`,
if selection of the node is allowed.
This method is overridden for `HTMLButtonElement` and
`HTMLInputElement`, to disallow selection of buttons and placeholders.
Fixes#579
We should only block the escape key from being sent to the web page if
the CloseWatcherManager actually closed something.
We use the escape key in the Inspector to cancel editing a DOM field.
This unconditional early return broke this feature.
The drag-and-drop processing model allows for users to drag around
either elements within the DOM or objects completely outside the DOM.
This drag event can either end without action (via cancellation or user
input), or in a drop event, where the dragged object is dropped onto
another element within the DOM.
The processing model is rather large. This implements enough of it to
allow the UI process to specifically handle dragging objects outside of
the DOM onto the DOM. For example, dragging an image from the OS file
manager onto a file-upload input element. This does not implement the
ability to drag DOM elements.
We can get away with using the forward declaration of EditEventHandler
in the header file (and avoid ~1k rebuilt files when EditEventHandler is
modified).
There will be an upcoming DragAndDropEventHandler class, so this patch
just sets things up so that the edit and drag-and-drop event handlers
are stored the same way in EventHandler.
This change causes the viewport to be treated as a "scroll frame,"
similar to how it already works for boxes with "overflow: scroll."
This means that, instead of encoding the viewport translation into a
display list, the items will be assigned the scroll frame id of the
viewport and then shifted by the scroll offset before execution. In the
future it will allow us to reuse a display list for repainting if only
scroll offset has changed.
As a side effect, it also removes the need for special handling of
"position: fixed" because compensating for the viewport offset while
painting or hit-testing is no longer necessary. Instead, anything
contained within a "position: fixed" element is simply not assigned
a scroll frame id, which means it is not shifted by the scroll offset.
Navigables are re-used for navigations within the same tab. Its current
ownership of the cursor position is a bit ad-hoc, so nothing in the spec
indicates when to reset the cursor, nor do we manually do so. So when a
cursor update happens on one page, that cursor is retained on the next
page.
Instead, let's have the document own the cursor. Each navigation results
in a new document, thus we don't need to worry about resetting cursors.
This also makes many of the callsites feel nicer. We were previously
often going from the node, to the document, to the navigable, to the
cursor. This patch removes the navigable hop.
The spec does not define activation behavior of ctrl/cmd clicks, so we
have to go a bit ad-hoc here. When an anchor element is clicked with the
cmd (on macOS) or ctrl (on other platforms) modifier pressed, we will
skip activation of that element and pass the event on to the chrome. We
still dispatch the event to allow scripts to cancel the event.
This allows selection to work within shadow roots and prevents the
selection being cleared if the mouse moves outside of the current
document or shadow root.
Now instead of sending the position in which the user entered the
tooltip area, send just the text, and let the chrome figure out how to
display it.
In the case of Qt, wait for 600 milliseconds of no mouse movement, then
display it under the mouse cursor.
This implements most of the CloseWatcher API from the html spec.
AbortSignal support is unimplemented.
Integration with dialogs and popovers is also unimplemented.
This now matches the behavior of did_request_link_context_menu and
friends. Previously the coordinates relative to the page rather than
viewport were sent to the chrome.
The main intention of this change is to have a consistent look and
behavior across all scrollbars, including elements with
`overflow: scroll` and `overflow: auto`, iframes, and a page.
Before:
- Page's scrollbar is painted by Browser (Qt/AppKit) using the
corresponding UI framework style,
- Both WebContent and Browser know the scroll position offset.
- WebContent uses did_request_scroll_to() IPC call to send updates.
- Browser uses set_viewport_rect() to send updates.
After:
- Page's scrollbar is painted on WebContent side using the same style as
currently used for elements with `overflow: scroll` and
`overflow: auto`. A nice side effects: scrollbars are now painted for
iframes, and page's scrollbar respects scrollbar-width CSS property.
- Only WebContent knows scroll position offset.
- did_request_scroll_to() is no longer used.
- set_viewport_rect() is changed to set_viewport_size().
Instead of TextPaintable fragments being an offset+length view into the
layout node, they are now a view into the paintable instead.
This removes an awkward time window where we'd have bogus state in text
fragments after layout invalidation but before relayout. It also makes
the code slightly nicer in general, since there's less mixing of layout
and painting concepts.