Stop converting between CSS and device pixels as part of rendering - the
display list should be as simple as possible, so convert to DevicePixels
once when constructing the display list.
Instead of using a custom paintable to draw the controls for video and
audio elements, we build them out of plain old HTML elements within a
shadow root.
This required a few hacks in the previous commits in order to allow a
replaced element to host children within a shadow root, but it's
fairly self-contained.
A big benefit is that we can drive all the UI updates off of plain old
DOM events (except the play button overlay on videos, which uses the
video element representation), so we can test our media and input event
handling more thoroughly. :^)
The control bar visibility is now more similar to how other browsers
handle it. It will show upon hovering over the element, but if the
cursor is kept still for more than a second, it will hide again. While
dragging, the controls remain visible, and will then hide after the
mouse button is released.
The icons have been redesigned from scratch, and the mute icon now
visualizes the volume level along with indicating the mute state.
We were previously not allowing the user to select text when the clicked
position represented a click-focusable area. This included text within
dialogs (as the dialog element is click-focusable) and labels (as the
associated input element would be considered click-focusable).
We now no longer consider associated input elements when clicking on a
label. This is handled separately already by the label's activation
behavior steps, so there is no loss of functionality here. In those
steps, though, we now no longer propagate the click event to the input
element if a selection was made during the click. This matches the
behavior of Firefox and Chrome.
With label elements no longer considered here, we can then enter the
character selection mode when click-focusable areas are clicked.
We were using a separately fired timer for auto scrolling ticks, but it
makes more sense to tie this into the rendering steps of which
`::run_the_scroll_steps()` is a part. Should fix the flaky
`Text/input/viewport-auto-scroll.html` test.
Fixes#7939.
Remove 11 heavy includes from Document.h that were only needed for
pointer/reference types (already forward-declared in Forward.h), and
extract the nested ViewportClient interface to a standalone header.
This reduces Document.h's recompilation cascade from ~1228 files to
~717 files (42% reduction). Headers like BrowsingContext.h that were
previously transitively included see even larger improvements (from
~1228 down to ~73 dependents).
When the mouse is dragged from inside a scrollable container to outside
of it, we now automatically scroll the container so the selection can be
extended. Scroll speed scales with the distance past the scrollport
edge, capped at a maximum. Edges close to the viewport boundary get a
wider activation zone so the speed ramp works predictably even when the
mouse has limited room to move.
The logic is encapsulated in AutoScrollHandler, which EventHandler
creates lazily on mouse selection start.
When triple clicking on text, we should select the entire paragraph, or
entire line in <input>s and <textarea>s. If the mouse button is held
down and the user starts dragging, the selection expands with additional
paragraphs or lines.
This expands on the work of Kai Wildberger (PR #7681) but was adjusted
for the work that happened previously to support double click + drag
moves and includes triple click support for our Qt UI.
Co-authored-by: Kai Wildberger <kiawildberger@gmail.com>
In EventHandler, we now keep track of a mouse selection mode which is
either None, Character or Word. By double clicking a word and
immediately dragging, you can now extend the selection word by word
instead of by character.
The AccumulatedVisualContext tree already handles the visual viewport
transform for hit testing (via transform_point_for_hit_test) and offset
computation (via inverse_transform_point). Passing viewport_position
(which has map_to_layout_viewport already applied) to these functions
caused a double-inversion during pinch-to-zoom.
compute_mouse_event_offset() only inverted the target element's own CSS
transform, ignoring ancestor transforms. This caused incorrect offsetX/
offsetY values when elements were nested inside transformed parents.
Use AccumulatedVisualContext::inverse_transform_point() to invert the
full ancestor transform chain instead.
Previously, click handling for labels was handled in layout and
painting code. This change implements activation_behavior on
HTMLLabelElement, which clicks and focuses the element.
Add webdriver_key_to_key_code() in Internals.cpp to properly translate
WebDriver special key codes (0xE000-0xE05D) to KeyCode values with
appropriate modifiers. This ensures keys like Enter, Backspace, and
arrow keys are handled correctly when sent via Internals::send_text().
In EventHandler::handle_keydown(), strip Mod_Keypad when determining
Enter key behavior since it only indicates key location (numpad vs
standard keyboard), not a behavior change. The modifier is still passed
through to KeyboardEvent for the location property.
This gains us 656 WPT subtest passes in `editing`.
When inserting a line break in a contenteditable with preformatted
white-space (pre, pre-line, pre-wrap), insert a newline character (\n)
instead of a <br> element. Use <br> only for padding at end of line to
ensure the cursor can be placed on the new line.
When delegating mouse events to iframes, coordinate transformations
were not accounting for scroll offsets. This caused clicks inside
iframes to be incorrectly positioned when the parent page was scrolled.
Add ElementResizeAction to Page (maybe there's a better place). It's
just a mousemove delegate that updates styles on the target element.
Add ChromeMetrics for zoom-invariant chrome like scrollbar thumb
thickness, resize gripper size, paddings, etc. It's not user-stylable
but separates basic concerns in a way that a visually gifted
designer unlike myself can adjust to taste.
These values are pre-divided by zoom factor so that PaintableBox can
continue using device_pixels_per_css_pixel calls as normal.
The adjusted metrics are computed on demand from Page multiple times
per paint cycle, which is not ideal but avoids lifetime management and
atomics. Maybe someone with more surety about the painting flow control
can improve this, but it won't be a huge win. If profiling shows
this slowing paints, then Ladybird is in good shape.
Update PaintableBox to draw the resize gripper and deconflict
the scrollbars. Set apropriate cursors for scrollbars and gripper in
mousemove. We override EventHandler's cursor handling because nothing
should ever come between a man and his resize gripper.
Chrome metrics use the CSSPixels class. This is good because it's
broadly compatible but bad because they're actually different units
when zoom is not 1.0. If that's a problem, we could make a new type
or just use double.
Model "viewport focus" with Document::focused_area == nullptr.
Focus.cpp:
When a blur occurs, remove the document entry from the old chain
before running the focusing steps. This ensures the document atop the
new chain is not discarded. Focus::run_focus_update_steps will then
set focused_area to nullptr, indicating viewport focus.
EventHandler.cpp:
Split hit testing in handle_mousedown. Use an exact hit test for
focus/blur decisions, and a subsequent cursor hit test for
selection/caret.
A version of this was added in a610639119
and reverted in 70671b4c11. The bugs
there (confusing scroll-to-position and scroll-by-delta, and not having
an execution context in some cases) have been fixed in this version.
This adds visit_edges(Cell::Visitor&) methods to various helper structs
that contain GC pointers, and makes sure they are called from owning
GC-heap-allocated objects as needed.
These were found by our Clang plugin after expanding its capabilities.
The added rules will be enforced by CI going forward.
Fixes crashing introduced in a610639 when `scroll_viewport_by_delta()`
is called from `EventHandler::handle_mousewheel()` and there's no
running execution context to grab current realm from to allocate a
promise.
If a script on the page cancels a mousemove event, we would return early
and neglect to update the cursor. This is seen regularly on "Diablo Web"
where they set the cursor to "none" on the main canvas, and also cancel
mousemove events.
Prevents observably calling Trusted Types, which can run arbitrary JS,
cause crashes due to use of MUST and allow arbitrary JS to modify
internal elements.
Adds pinch event handling that adjusts the VisualViewport scale and
offset. VisualViewport's (offset, scale) is then used to construct a
transformation matrix which is applied before display list execution.
Implements spec algorithm for viewport scrolling that first checks if
it's possible to use delta to move the visual viewport before falling
back to scrolling the layout viewport. This is a part of pinch-to-zoom
support.
...before falling back to containing block. Fixes a bug when we can't
scroll innermost scrollable element, because wheel event dispatching
immediately falls back to containing block.
We have to prevent from including any SDL headers in LibWeb headers.
Otherwise there will be transitive Windows.h includes that will
re-declare some of our existing forward decls/defines in
LibCore/SocketAddressWindows.h
And make it a DOM::Node, not DOM::Element. This makes everything flow
much better, such as spec texts that explicitly mention "focused area"
as the fact that we don't necessarily need to traverse a tree of
elements, since a Node can be focusable as well.
Eventually this will need to be a struct with a separate "focused area"
and "DOM anchor", but this change will make it easier to achieve that.