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.
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.
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.
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.
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.
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.
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.
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.
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.
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>
This introduces a simple FileDownloader to download files in the UI
process from RequestServer. We use this to download the context menu
image - this download is likely to hit the disk cache.
We currently always save screenshots to the Downloads folder. We will
now always ask for a save location.
This will just let an upcoming feature to save images from web pages
behave the same way. We will want the user to be able to choose a file
name, since the file name from the URL might be nonsense or already
exist.
We used to have several of our own window types to close. We now only
have Tab windows. Our delegate handler is invoked even for windows such
file picker modals. The result was that we would close the modal window,
but the application would be left in a modal-active state.
We now behave like Safari. If cmd+w is pressed while a file picker modal
is open, no window is closed, and an error beep is played by AppKit.
Add a thread-safe deferred_invoke() API on WeakEventLoopReference that
queues work onto the owning thread's event queue and wakes that thread
via EventLoopManager hooks. This avoids calling wake() from foreign
threads during teardown.
Implement current_thread_handle()/wake_thread() in each backend and
track per-thread data so handles are validated before waking:
- Unix: wake via per-thread wake pipe
- Windows: wake via thread wake event
- macOS: wake via stored CFRunLoopRef
- Qt: wake via event target or QEventLoop::wakeUp()
- Android: wake via stored ALooper
The base class deferred_invoke() adds work to the thread event queue
and calls wake(), but that's not sufficient for CFRunLoop. We also
need to signal the deferred_source via CFRunLoopSourceSignal() so the
run loop knows to call its perform callback which processes the queue.
Without this signal, deferred invocations posted from other threads
(such as IPC I/O threads) would not be processed, causing deadlocks
when waiting for sync IPC responses.
wake() was using CFRunLoopGetCurrent() which returns the calling
thread's run loop. When called from an I/O thread to wake the main
thread's event loop, this would wake the wrong run loop.
Fixed by using the stored m_impl->run_loop which is captured at
construction time and refers to the event loop's owning thread.
When the user focuses the address bar and presses Escape, the original
URL is now restored. Previously, the address bar would show whatever
text the user had typed, or be empty if they had cleared it.
Clicking outside the address bar keeps the typed text (matching
Chrome/Firefox behavior) — only Escape restores the original URL.
Fixes https://github.com/LadybirdBrowser/ladybird/issues/7264
Once upon a time, we needed the UI-specific event loops outside of the
UI process. This is no longer the case. Let's move the event loops back
to the UI folder to remove the awkward interface library we were left
with.
When a shortcut is an alternative key, macOS first fires a key event for
the primary key. Then if it is not handled, it fires an event for the
alternative key. For example, we see these two events in a row when we
press cmd and =/+:
NSEvent: type=KeyDown flags=0x100108 chars="=" keyCode=25
NSEvent: type=KeyDown flags=0x100108 chars="+" keyCode=24
For dead key processing, we don't handle these events right away. By the
time we get to doCommandBySelector, when we call [NSApp currentEvent],
we see the following events:
NSEvent: type=KeyDown flags=0x100108 chars="=" keyCode=24
NSEvent: type=AppDefined flags=0
The AppDefined event is internally posted by our event loop. So it seems
we cannot rely on currentEvent being the key-down event we are looking
for.
Instead, we can store the key-down event that we last saw.
When a new tab is created, the location bar should automatically
receive keyboard focus so users can immediately start typing a URL.
However, the onURLChange callback was stealing focus back to the
web view when loading the new tab page.
This fix:
1. Calls focusLocationToolbarItem when activating a new tab
2. Prevents onURLChange from stealing focus when loading the new
tab page URL
Fixes#1512
The autocomplete popover on macOS was stealing focus from the location
bar when suggestions were displayed. This change saves the current
first responder before showing the popover and restores it immediately
after, ensuring the user can continue typing without interruption.
When trying to build Ladybird on macOS 14.3, it fails with the error:
```
No visible @interface for 'NSToolbar' declares the selector
'setAllowsDisplayModeCustomization:' (clang arc_may_not_respond)
```
This is caused by macOS < 15 not having the @interface definition for
in NSToolbar for setAllowsUserCustomization:(BOOL).
By dynamically calling the method, we can avoid the error altogether.
This prevents the AppKit UI from sending unknown MIME types to LibWeb,
where we would previously blindly dereference the result of parsing the
MIME string. We now ignore unknown MIME types as well.