Picker input starts in each platform UI, while protocol state lives in
LibWebView. Route movement, primary clicks, Escape, and leave events
through the WebView picker API so each frontend drives the same hit-test
and picker-event path.
The selected click is consumed because picker mode uses it to choose an
element for DevTools rather than activate page content.
AppKit routes normal mouse input through WebViewBridge, which converts
view points to device pixels before forwarding to LibWeb. Picker input
bypasses that enqueue path, so apply the same scaling before requesting
a picker hit test.
WebContentView scales its painter into device-pixel coordinates before
painting page bitmaps. When no page bitmap was available, the fallback
fill still used the widget rect in CSS pixels. On high-DPI displays, it
only filled the top-left portion of the viewport.
Use the stored device-pixel viewport size for the no-bitmap fill and for
the fallback area around a smaller bitmap.
This abstraction is needed for an upcoming commit to make the UI layer
react to changes in menu visibility. It turned out to be a nice cleanup
in any case.
We attempted to paint a preview of the dragged tab, but this only worked
for the first tab. For other tabs, we were passing a negative offset to
QWidget::render, causing the preview to render transparently.
We were trying to handle this in TabWidget::event, but this mouse event
is now delivered to the child TabBar, where the event is accepted and
thus not propagated further.
Use the page palette for default selection backgrounds instead of a
hardcoded blue color. Tint opaque selection backgrounds before painting.
Selected images stay visible through the overlay.
Use SelectionText for default selected text and HighlightText. Authored
::selection colors still take precedence. On macOS, use a light
translucent color matching other engines.
Update selection and system-color baselines for the themed colors. Add
macOS-specific screenshot baselines for tests whose expectations depend
on system colors.
The browser process forwards the maximum frame rate to WebContent, but
the compositor process had no copy of that display timing metadata or
the display identity needed by compositor-local vsync.
Add one compositor control IPC message carrying the optional display ID
and refresh rate together, and store both values on each compositor
context.
BrowserWindow only connected QWindow::screenChanged while setting up
the non-Wayland DPI path. On Qt Wayland this path is skipped
because device-pixel-ratio changes are delivered to the window instead
of QScreen. A window could stay associated with the screen reported
during construction until the native QWindow exists, so refresh-rate
updates were missed when the window moved between outputs.
Separate refresh-rate and screen tracking from the DPI-specific signal
handling. Connect QScreen::refreshRateChanged for the current screen on
all platforms, connect QWindow::screenChanged when the native window
becomes available, and use ScreenChangeInternal as an additional update
hook. The DPI signal remains disabled for Qt Wayland, where
DevicePixelRatioChange is still handled through BrowserWindow::event().
Custom-paint bookmark bar buttons so favicons and titles are spaced and
vertically centered consistently. Qt’s default QToolButton label layout
does not expose enough control over the icon/text gap or pixel-level
vertical alignment needed for the tightened bookmarks bar.
These both require the same helper processes, and maintaining the same
list in multiple places is error-prone. For example, I spent too long
debugging why test-web was crashing, when the issue was that Compositor
hadn't been rebuilt as test-web didn't depend on it. That problem can
no longer happen if we define the same dependency list for both.
Before the recent Qt overhaul, we had no control over the position of
the tab close button (without drawing/maintaining it ourselves, which we
now do). Qt automatically placed the tab close button on the left side
of the tab bar on macOS, and as such, we had to go out of our way to
place the audio state button on the right side. See:
dd54780d5e
We now consistently place the audio state button on the left and the tab
close button on the right. Qt still attempts to draw its own tab close
button on the left, so we also explicitly set that to null initially.
Generate DPR-tagged pixmap variants for generated icons (this includes
chrome icons, TVG icons, and bookmark favicons). Previously, these icons
would render very blurry on macOS.
Use matching top and right padding around the tab strip window controls
when the menu bar is hidden, while keeping the existing left spacing for
tabs and the resize edge.
Add a menu bar corner widget for the frameless window controls when
the Qt menu bar is visible. Hide the tab strip controls in that mode
so the buttons stay at the top-right edge of the window, and remove
the separator below the menu bar so the chrome reads as one surface.
Use compact menu bar controls and keep the right padding tight so the
buttons align with the edge without changing the hidden-menu layout.
Start a platform window move when pressing empty space in the Qt menu
bar. Toggle maximized state on double-click, matching the tab strip.
Menu actions keep their normal behavior, and resize edges still take
precedence before starting a move.
Add a shared WindowControlButton for the frameless window controls. It
clears the pressed visual state when the pointer leaves during a press,
and restores it on re-entry.
Also reset transient button state around double-clicks so pressed
styling cannot get stuck after the window state changes.
Drop the PNG resources that were only used for menu action icons.
Keep the app icons and directory listing icons that still have direct
resource references, and stop copying the now-empty browser icon set.
Let application actions opt out of icon creation when created for
menus. Use that path for browser menus, tab context menus, and
WebView-backed popup menus.
Keep icon creation for toolbar and bookmarks bar actions, where the
same helper still provides visible button icons. Remove the icon setup
that is now only used by menu actions, and shrink popup menu item
padding now that there is no icon column.
Add application-level QMenu styling using the existing chrome palette so
menu bar dropdowns, hamburger menus, context menus, and other popups use
the same colors and item states. Refresh the application stylesheet when
the Qt palette or theme changes.
Add a QMenuBar stylesheet that uses the same chrome palette as the tab
strip and navigation toolbar. Apply it from BrowserWindow and refresh it
when the palette changes so the optional menu bar matches the frameless
window chrome.
Seed the page background fallback from the platform color scheme before
WebContent has painted. Update the same fallback when the platform theme
changes, and when the preferred color scheme is forced through the debug
menu, so unpainted web view areas use the matching Canvas color.
Commit 218d82cb65 added support for
pasting text with the middle mouse button. But primary pasting is
actually meant to interact with the "selection" clipboard, not the
text clipboard.
The bookmarks bar sticks out a bit too much in its own container with
its own background color. This adds a common container for both bars
so that we can apply the background color gradient to that common
container. This naturally handles showing/hiding the bookmarks bar
gracefully.
This is never referenced outside the constructor; we don't need to store
a pointer to it. (I had accidentally removed it in a subsequent commit,
then realized we didn't need it anyways).
Send hidden and visible system visibility updates from explicit tab
switch paths instead of relying only on widget show and hide events. Tab
containers can keep inactive web views mapped, which leaves documents
visible from WebContent's perspective and lets animated images keep
running in background tabs.
Use AppKit window occlusion state rather than the app-wide active tab.
That keeps still-visible background windows in the visible document
state. Avoid resending duplicate visibility updates from
ViewImplementation so existing widget visibility notifications remain
harmless.
Trackpad pinches applied directly as visual viewport zoom, so pages
could not observe the gesture or cancel the browser default. Canvas
apps such as maps expect the ctrl-wheel path instead.
NSMagnificationGestureRecognizer exposes magnification as an accumulated
value for the current gesture. Ladybird kept its own previous value in
WebViewBridge and emitted the residual delta when the gesture ended, so
the Web input pipeline did not receive the same per-update shape as the
native changed events.
Reset the recognizer's magnification accumulator after changed updates
and skip begin/end states. This keeps the AppKit side responsible for
delivering concrete scale changes and removes the bridge pinch state.
Let browser chrome shortcuts for tab and window management bypass the
WebContent shortcut override path. This makes new tab, new window,
reopen closed tab, tab switching, close tab, and quit dispatch through
Qt immediately instead of waiting for page event handling.
Track whether WebContent still needs a beforeunload check and let the
frontends immediately remove a tab or window when no prompt can be
shown. WebContent still receives the close request so pagehide, unload,
and cleanup steps can run.
When the visible view is removed immediately, keep detached ownership of
the WebContent page until it reports that the top-level traversable
closed. If no acknowledgement arrives, release detached ownership and
ask ProcessManager to shut down the unused WebContent process.
When a child process terminates on Windows, the QWinEventNotifier
emits the activated signal it its event handler. Before this
happens, we need to call setEnabled(false) to unregister the
wait object from the Windows thread pool. setEnabled(false)
also causes us to wait for any outstanding callbacks to complete
and then prevents QWinEventNotifier from trying to use the closed
handle.
This seems to follow the pattern from Qt where the destructor also
calls setEnabled(false) before destruction.
Keep late about:newtab load notifications from clearing hidden location
text after the user has started typing. Let LocationEdit own cursor
placement for normal URL display so load-start updates do not disturb
inline autocomplete selection.
Draw back and forward icons as filled stroked paths so the arrow tips do
not accumulate alpha where segments meet. Increase their apparent size,
shift the menu glyph down slightly, and raise the new tab icon to align
with the tab strip.
Keep active and hovered tabs on the same geometry while tightening the
surrounding chrome rhythm. Reduce tab and toolbar padding, shorten the
navigation toolbar and omnibox, center the new tab button, and enlarge
tab close buttons to match the refined tab sizing.
Define neutral chrome material anchors in one place and derive shared
surface, hover, pressed, and border colors from them. This removes
system accent color from chrome hover fills and gives the tab strip,
toolbar, and omnibox separate material roles in both color schemes.
The `WA_OpaquePaintEvent` attribute on `BrowserWindow` means Qt no
longer fills widget backgrounds automatically. Plain `QWidget`
subclasses do not paint their stylesheet backgrounds by default, so the
find in page bar lost its background color and did not clear its state
correctly between repeats . Setting `WA_StyledBackground` tells Qt to
honor the stylesheet background property in the widget's paint cycle.
Previously, a tab would only detach into a new window when dropped
outside the source window entirely. Dropping a tab onto the content
area of any browser window would instead silently move it to the end of
that window's tab bar.
TabWidget now rejects tab drag events outside its tab bar row, and the
detach condition checks the tab bar row bounds rather than the full
window geometry. This means dropping a tab anywhere that is not a tab
bar row opens a new window at the cursor position.
Keep the pending chrome style refresh marker set while the deferred
refresh runs. Applying the refreshed stylesheet can synchronously
deliver palette/style change events, and clearing the marker first lets
those self-induced events enqueue another immediate refresh.
This prevents the Qt chrome from spinning through repeated stylesheet
updates and repainting the location field shadow at idle.
Refresh Qt chrome palettes from the current color scheme when the
application palette changes. Recompute autocomplete and location field
colors and repaint WebContent immediately so theme changes do not
require another interaction or restart.
Refine the material colors, tab integration, toolbar spacing, and
custom glyphs used by the Qt browser chrome. Keep the visual density
compact while making the controls feel clearer and less toolkit-like.
Mark the window opaque and show resize cursors for frameless chrome.
Allow custom Qt tabs to be reordered, detached, and reparented to
other browser windows while preserving the existing tab state. Draw a
full-tab drag preview and keep tab drags from being interpreted as
frameless window moves.
Add shared chrome styling helpers and use them to paint the Linux
browser tab strip and surrounding toolbar widgets. Make the browser
window frameless on Qt so the tabs, toolbar, and window controls can
share one integrated chrome surface.
Notify the UI process with the solid canvas background color recorded
for the top-level document. This is the Canvas system color with the
effective document background composited over it, matching the color
used before normal painting.
Store that color on the view and use it when AppKit, Qt, and Gtk need
to fill areas exposed while an older bitmap is still on screen during a
window resize.
Register debug.process.show_web_content_process_id and use it to append
the active WebContent process ID to tab titles and tab tooltips in the
Qt, Gtk, and AppKit frontends.
Keep the stored page title unchanged so toggling the preference can
refresh each tab without waiting for a new title update.
Add a small registry for config variables backed by JsonValue defaults.
Persist values under configVariables and expose the registry through the
Advanced tab in about:settings so every key remains visible and
filterable.
Use the default JsonValue type for validation instead of maintaining a
parallel type enum.
Replace the local substring matcher with the adblock-rust engine exposed
through the dedicated content blocker Rust FFI. Keep the previous engine
when a replacement list cannot be parsed.
Pass shared list buffers directly into Rust instead of building a
duplicate C++ vector of lines first. Generate cosmetic CSS through the
Rust matcher, including generic class and id selectors collected from
shadow-including descendants.
Keep a supplemental index for generic cosmetic selector-list rules.
adblock-rust indexes these rules under the first class or id token only,
so also key them by later simple class and id selectors in the list.
Update ContentBlocker coverage for rule options, exceptions, third-party
checks, blob and file URLs, invalid list replacement, filtering toggles,
cosmetic CSS, and generic selector-list cosmetics.
The compositor is moving into a dedicated helper process. That requires
a process to launch, channels for Browser and WebContent to talk to it
over, and client proxies on each side. Land all of that as an inert
scaffold first, gated behind --enable-compositor-process, so the default
rendering path is unchanged and later commits can fill in the protocol,
the service-side state, and the runtime switch against a stable target.
Rename the local content blocking implementation and its tests from
ContentFilter to ContentBlocker while keeping the existing substring
matcher backend and behavior.
Update the WebContent IPC method, WebView option names, debug toggle,
and default config file name to use content blocker terminology.
This factory is only used by the UI. An upcoming commit will make it
depend on LibFileSystem. LibWeb currently does not link LibFileSystem,
and doing so would push LibWeb in the wrong direction (we should be
doing less file IO in LibWeb, not more).
Move keyboard focus to the web view when the user commits the
location field. Relying on URL-change callbacks is unreliable because
ViewImplementation::load() stores the requested URL before WebContent
reports it, so same-URL reports are suppressed before frontends can
perform their focus handoff.
Keep the new-tab page behavior unchanged: creating a blank tab still
focuses the location field until the user starts a navigation.
Use a small proxy style for the tab label control so QTabBar still
owns tab layout, scrolling, buttons, dragging, and native tab shapes.
Only the text draw flags change to use left alignment.
Show visible tab icons as loading while the focused location field
keeps its existing page-level loading state. Cache the favicon under the
spinner so each tab restores its page icon when loading finishes.
Reuse the Qt spinner drawing for the location field and tab strip, and
draw the AppKit spinner into the attributed tab title.
Use the leading icon in the location field as a contextual page
indicator. While a page is loading, the field shows an indeterminate
activity indicator. Once loading finishes, it shows the page favicon
when available and falls back to the generic page icon where the toolkit
has one.
Keep the GTK implementation aligned with the other frontends by using
that slot only for loading and favicon state, without a separate
security icon.
This keeps single-tab windows from losing all visual feedback when the
tab bar is hidden, without adding a fake progress bar.
Create new windows with the size of the previously active window, and
maximized if it was maximized. This matches the behaviour of other
browsers, and solves an annoying issue where new windows would always
be tiny.
Application::new_window() now takes a WindowConfiguration struct that
lets you specify the position, size, and maximized-ness of the window.
All previous callers of new_window() now use this instead of modifying
the window state afterwards.
WindowConfiguration is slightly awkwardly broken up into individual x/y
and width/height fields instead of a position and a size, for the sake
of compatibility with HTML::WebViewHints.
Wheel deltas were truncated to int at the platform input boundary,
which dropped the sub-pixel tail of trackpad momentum scrolls. Each
NSEvent's scrollingDeltaY arrived as a CGFloat, got cast to int, and
flowed through IPC, EventHandler, and PaintableBox::scroll_by as int,
losing fractional information that never came back.
Widen Web::MouseEvent::wheel_delta_{x,y} to double and propagate
through Page, EventHandler, Paintable, PaintableBox, and the AppKit,
Qt, and GTK input paths.
Other engines converge on roughly 40 pixels per line as the per-line
step for discrete mouse wheel input (Chrome's kWheelDelta of 120 over
3 lines, WebKit's Scrollbar::pixelsPerLineStep of 40, Chrome's macOS
kScrollbarPixelsPerCocoaTick of 40). At a step of 24 we were scrolling
about 60% as far per tick as Chrome on the same input, which made the
browser feel sluggish under a regular wheel.
Bumping the constant to 40 in the Qt, Gtk, and AppKit frontends puts
us at 120 pixels per discrete tick on Linux (3 lines * 40 pixels) and
aligns the macOS imprecise-scroll multiplier with WebKit and Chrome.
High-precision (trackpad) input is unaffected on all platforms, since
those paths use pixel deltas directly without applying a multiplier.
Autocomplete rows were sized using the table view bounds instead of the
visible scroll view width, which caused the rounded selection highlight
to be clipped on the right edge.
This commit splits out synchronization primitives from LibThreading into
LibSync. This is because LibThreading depends on LibCore, while LibCore
needs the synchronization primitives from LibThreading. This worked
while they were header only, but when I tried to add an implementation
file it ran into the circular dependency. To abstract away the pthread
implementation using cpp files is necessary so the synchronization
primitives were moved to a separate library.
DecodedImageFrame only wraps a ref-counted Bitmap and color-space
metadata. The frame object itself does not provide shared mutable
state or lifetime ownership beyond those members, so ref-counting it
adds an unnecessary layer of indirection.
Decoded image data should not continue to traffic in ImmutableBitmap now
that the bitmap wrapper is being retired. Introduce DecodedImageFrame as
the paintable decoded-image unit and store a Bitmap plus ColorSpace in
it directly.
Thread the new frame type through decoded image data, display-list
image commands, filters, canvas drawImage, patterns, WebGL texture
upload, and CSS/SVG image consumers. ImmutableBitmap remains only at
the legacy boundaries that still need it, such as HTML video snapshots
and callers that explicitly ask for a bitmap snapshot.
This keeps color-space ownership with the decoded frame while making
the expensive or legacy ImmutableBitmap path explicit at the few call
sites that still need it.
GDK4 scroll events do not carry reliable pointer coordinates on Wayland
(gdk_event_get_position returns INT_MIN). Track the last known pointer
position from the motion controller and use it when building the wheel
event, matching the approach used by WebKit GTK.
This fixes mouse-wheel scrolling and scrollbar interaction in pages
with overflow containers such as the pdf.js viewer.
Previously, if search was disabled, entering non-URL text would just
silently drop the search query (and on Qt, we would reload the current
URL). We now detect that the query did not result in a navigation and
load an error page instead, which directs the user to enable search.
Passing the browser command line and executable path to every WebContent
process just in case we load about:version always felt a bit weird. We
now use the WebUI framework to load this information on demand.
In commit b9c6263408, I had forgotten we
had this wrapper script to handle the eval magic for us. We just need
to forward the --clang-only argument to the underlying python script.
Add the complete browser UI:
- BrowserWindow: AdwHeaderBar with navigation, tab management via
AdwTabView/AdwTabBar, find-in-page, fullscreen, zoom, D-Bus
single-instance with open/activate handlers
- Tab: WebContentView lifecycle, ViewImplementation callbacks for
title, URL, favicon, cursor, tooltips, dialogs, window management
- LadybirdBrowserWindow: GtkBuilder template widget with toolbar,
tab bar, find bar, devtools banner, and hamburger menu
- LadybirdLocationEntry: URL entry with autocomplete, domain
highlighting, and security icon
- Menu: GAction-based context menus and application menu with
keyboard accelerators
- Dialogs: JS alert/confirm/prompt (AdwAlertDialog), color picker,
file picker, select dropdown, download save dialog, toast
- GtkBuilder .ui resources for browser window, location entry
completions, and list popovers
Updates Application and main.cpp to create browser windows and
handle D-Bus activation from remote instances.
Add the core infrastructure for the GTK frontend:
- EventLoopImplementationGtk: GLib main loop integration with
Core::EventLoop, timers, notifiers, and signal handling
- GObjectPtr RAII wrapper for GObject lifecycle management
- Application subclass with AdwApplication and D-Bus registration
- Minimal main.cpp entry point
The application starts and runs the event loop but does not yet
open any windows.
Tabs opened from links on AppKit can be created before their URL is
loaded. That left background open-url tabs with the location field as
their stored responder, so switching to them later restored address-bar
focus instead of web content focus.
Track each tab's preferred responder, restore it when AppKit makes
the tab key, and mark page-backed open-url tabs to prefer the web view.
Blank new-tab pages still keep the location field focused.
Restore the active tab's remembered focus widget when switching tabs.
If the tab has no remembered focused child, fall back to the web
view so keyboard scrolling works immediately in tabs opened from
links.
This keeps the existing new-tab location bar behavior while making
background-opened pages respond to keyboard inputs as soon as they
are activated.
Synthesize an extra AutocompleteSuggestion at the top of the Search
Suggestions section whenever there is a configured search engine and
the typed query is not URL-shaped.
Use the query as the row's primary text, carry a "Search with <engine>"
subtitle, and render that subtitle in the AppKit and Qt popups so the
explicit search fallback stays visible and readable even when history
fills the list.
Replace the old QCompleter-based popup with a custom list that renders
AutocompleteSuggestion rows with titles/URLs/section headers/favicons.
Parent the popup inside the window so it does not steal focus, route
keyboard and mouse activation through WebView::Autocomplete, and add
inline completion with backspace suppression and top-row selection.
Render AutocompleteSuggestion rows with section headers, favicons,
titles, and secondary text in the child-window popup instead of just
plain strings.
Move the AppKit popup and inline completion onto the shared suggestion
model, and share the base64 PNG decoding helper with the application
menu icon loading path.
Replace the AppKit popover because it becomes key and steals focus from
the location field while suggestions are visible. Use a borderless
child window anchored to the toolbar item instead, and dismiss it on
resize or focus changes so the browser chrome stays predictable.
Teach LibWebView autocomplete to query HistoryStore before falling back
to remote engines and move the wiring out of the AppKit frontend.
Refine matching so scheme and www. boilerplate do not dominate results,
short title and substring queries stay quiet, and history tracing can
explain what the ranking code is doing.
The bytecode interpreter only needed the running execution context,
but still threaded a separate Interpreter object through both the C++
and asm entry points. Move that state and the bytecode execution
helpers onto VM instead, and teach the asm generator and slow paths to
use VM directly.
Qt does not use IOCP's in their underlying Windows event loop
implementation; however, QWinEventNotifier allows us to register a wait
on a process handle that has SYNCHRONIZE access rights. This means an
event will be signalled when that process terminates which emits the
QWinEventNotifier::activated signal.
Co-authored-by: Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
This page renders the bookmarks as a tree and hook context menu events
up to the UI's bookmarks bar context menus to allow editing bookmarks.
Users can also drag-and-drop bookmark items around.
Generalize the backing store sharing abstraction into SharedImage, which
represents shared GPU memory independently of Skia and can be used to
share memory between different processes or different GPU contexts.
AppKit doesn't need the bookmarks bar reorganization that Qt needed to
handle more mouse events. But let's rename these member fields to match
what they are now called in Qt.
In addition to Ctrl+PageDown and Ctrl+PageUp, also support Ctrl+Tab and
Ctrl+Shift+Tab to switch between browser tabs. This is the common
behavior in other browsers.
Unfortunately, the NSMenu-based bookmarks implementation cannot support
context menus within arbitrary folders. The NSMenu consumes the right-
click events during its own menu tracking, and we are not able to see
those events from our interface.
This patch replaces the NSMenu for folders with a custom NSPopup. This
implementation makes it much easier to handle context menus.
This allows us to get rid of the annoying Build/.../Lagom directory
and helps to deduplicate a bunch of logic while also drastically
simplifying everything.
This is largely based of off the work done by Andrew Kaster in #5918.
Having this toggle makes much more sense, especially if there will be
more UIs in the future.
Co-authored-by: Andrew Kaster <andrew@ladybird.org>
The parent owner of the TabBar changes as it is added to layout objects
within other widgets. So the parent we set at the beginning is no longer
the parent when we right-click on a tab. We now just store the TabWidget
directly.
QTabWidget's built-in tab bar management made it difficult to properly
position the new tab button, and caused visual artifacts with the
separator line across themes.
Replace QTabWidget with a custom TabWidget that places a QTabBar
alongside an explicit new tab button. This us gives full control over
the tab bar row layout.
We initially create the search field with no frame or size constraints.
The causes AppKit to log verbose warnings at start up. Here, we set an
intrinsic size initially, which becomes overridden once the window has
an actual size.
The HamburgerMenu subclass overrode showEvent() to reposition the menu
using move() after QMenu::popup() had already shown it. On the first
open, rect().width() could return a stale value, causing the menu to
appear detached from the button and truncated to a single item. The
move() call also bypassed Qt's screen boundary adjustments.
Instead, we can replace the HamburgerMenu with a normal QMenu, and use a
custom HamburgerButton class for the menu's tool button. In this class,
we override mousePressEvent() to call popup() with the correct right-
aligned position before the menu is shown. This lets Qt manage sizing
and screen boundaries correctly.
Changing Ctrl+click to a secondary click is incorrect. It prevents
sites from using Ctrl+click themselves. Instead, just maybe open the
context menu in mousedown for primary clicks with Ctrl pressed.
Fixes the autofire shortcut not working in the Humble Mozilla Bundle's
asm.js FTL.
This moves normal/double/triple click checking into WebContent, the
client only has to send a click count in order to activate a double
or triple click in the content. This means that the AppKit UI will no
longer fire multiple double clicks when clicking in place more than 3
times. This matches the behavior of other browsers on macOS.
We will now also fire the click event regardless of whether a dblclick
event will follow, as the spec requires.
When an element is fullscreened, we now hide the application menu and
location bar. The user can hover over the top of the window to make
them show. When fullscreen is requested for the application directly
from the UI, we do not hide the location bar. This matches Safari's
behavior.
We were incorrectly calling presentMetalFrame while force-cpu-painting
was enabled, so updateLayer never had any chance to be called.
However, in addition to that, it turned out that there's a subclass of
CALayer that is used by NSView by default which cannot be created in a
makeBackingLayer override, so updateLayer was never going to be called
anyway. In order to update the layer at the requisite time, a new
LadybirdWebViewContentLayer had to be added to simply call the delegate
on our view.
Setting the contentsRect on the layer was also incorrect, and caused
the contents to shift and stretch until WebContent's backing stores
shrank to fit the new view size.
WebView::FontPlugin was the only implementation of the abstract
FontPlugin base class. Its dependencies (LibGfx, LibCore) are
already visible to LibWeb.
Remove the virtual dispatch by making FontPlugin concrete and
absorbing the WebView::FontPlugin implementation directly.
EventLoopPluginSerenity was the only implementation of the abstract
EventLoopPlugin base class. Its methods simply wrapped Core::EventLoop
calls with GC function unwrapping.
Remove the virtual dispatch by making EventLoopPlugin concrete and
absorbing the EventLoopPluginSerenity implementation directly.
This adds the following features to the Qt UI:
- An "exit fullscreen" button that displays when entering fullscreen
This will disappear after a certain amount of time. Moving the
cursor to the top of the screen will make it reappear.
- Switching tabs exits fullscreen
The look and feel of this is preliminary and should be expanded upon
in the future.
These IPC methods should be expanded in the future to allow WebContent
to specify what UI elements should be kept/removed, for example, the
navigation UI.
The set_viewport_size and set_device_pixel_ratio IPC messages were sent
separately, potentially causing a race condition when the DPR changes
(e.g. moving a window between screens): the DPR message would arrive
and use a stale viewport size, computing a temporarily wrong CSS
viewport. Combine both into a single set_viewport IPC that updates the
device viewport size and DPR together.
This aligns our behaviour closer to other browsers, which
_mostly_ consider file scheme URLs as opaque. For test
purposes, allow overriding this behaviour with a commandline
flag.
Instead of copying the Bitmap that wraps the IOSurface, we can just
present the IOSurface directly. This significantly reduces CPU usage in
the UI process, particularly at high refresh rates such as 120Hz where
it would saturate a full CPU core.
This is done by using CAMetalLayer and blitting the IOSurface to the
next drawable buffer, which handles triple buffering, locking the
IOSurface and vsync automatically. This also allows the Metal HUD to
work, but the only accurate stat is the frame intervals/FPS because
it's in the UI layer, not WebContent. However, that's still useful
to detect frame drops.