The setting for the search engine to use is currently ephemeral. Once we
have a settings dialog, we can implement this setting there, and persist
that setting.
The default behavior of QPushButton is much closer to what we want from
a drop-down menu, as shown in the QPushButton::setMenu documentation:
https://doc.qt.io/qt-6/qpushbutton.html#setMenu
This also results in much less of a "squished" look than before.
The current size is too small to be able to read the new tab URL. Use
the `resize` API rather than setting a fixed-size as well, to allow the
user to resize the dialog themselves.
This lets the user zoom in and out on a web page using the View menu or
keyboard shortcuts. This does not implement zooming with ctrl+scroll.
In the future, it'd be nice to embed the zoom level display inside the
location toolbar. But to do that, we will need to invent our own custom
search field and all of the UI classes (controller, cell, etc.) to draw
the field. So for now, this places the zoom level display to the right
of the location toolbar.
This is in the spirit of commit a4692a6c978a6e66d171e003063449790a6c5879
(and the history behind that commit).
We will need to perform lookups from an integral node ID to the JSON for
that node frequently in the Inspector. We will also need to traverse the
DOM tree from a node, through its ancestors, to the root node. These are
essentially the same maps stored by the Qt Inspector widget.
This commit includes only fetching the DOM tree from the WebContent
process and displaying it in an NSOutlineView. The displayed tree
includes some basic styling (e.g. colors).
Instead of having an annoying loop that constantly reschedules a
Core::EventLoop trigger, have the ALooperEventLoopManager do it itself
in the did_post_event() function.
We cannot simply re-use the Unix implementation directly because that
implementation expects to actually be called all the time in order to
service timers. If you don't call its' pump() method, timers do not get
triggered. So, we do still need the seconary thread for Timers that was
added earlier.
Similar to the RequestServer, bind this from the WebContentService
implementation and have it work the same way. Deduplicate some code
while we're here.
Add a RequestServerService class that uses the LadybirdServiceBase class
added previously. Bind to it from the WebContentService's service_main()
during startup.
Create LadybirdServiceBase to hold the standard "set resource dir" and
"init ipc sockets" service functionality that will be common between the
WebContent, RequestServer, and WebSocket services.
Refactor the handler class slightly to avoid the HandlerLeak lint by
making the class a static class inside the companion object and use a
WeakReference to the service instead of a strong one.
After moving to navigables, we started reusing the code that populates
session history entries with the srcdoc attribute value from iframes
in `Page::load_html()` for loading HTML.
This change addresses a crash in `determine_the_origin` which occurred
because this method expected the URL to be `about:srcdoc` if we also
provided HTML content (previously, it was the URL passed along with the
HTML content into `load_html()`).
The FIXME at the bottom of Timer.nativeRun was on the money, and was
the cause of some leaked timers. Solve the issue by passing the
EventLoopImplementation to the JVM Timer object and retrieving it's
thread_local timer list, and posting the events to the Impl rather than
trying to invoke the receiver's timer event callback directly in
the Timer thread. Because the implementation of
ALooperEventLoopManager::did_post_event() reaches for
EventLoop::current(), we also need to make sure that the timer thread
acutally *has* an EventLoop, even if we don't expect to use it for
anything. And we need to make sure to pump it to clear out any of the
poke() invocations sending 4 bytes to the wake pipe for that thread.
This function used to live in AndroidPlatform.cpp, but was removed
during the transition to the new app. We still need to extract the
assets from the tarball that CMake creates. At least, until we come
up with a generic "Resource" concept in LibCore.
Timers run in their own thread, to take advantage of existing Java
Executor features. By hooking into ALooper, we can spin the main
Activity's UI thread event loop without causing a fuss, or spinning the
CPU by just polling our event loop constantly.
This will let us spawn a new process for an Android Service to handle
all our WebContent needs. The ServiceConnection is manged by each
WebView. The lifecycle of the Service is not quite clear yet, but each
bindService call will get a unique Messenger that can be used to
transfer the WebContent side of the LibIPC socketpair we use in other
ports.
The simple smoke test makes sure that we can boot up an android emulator
with our package in it, and that the WebView is visible on boot.
More tests to come with more features :^)
This folder is gitignored because we copy a generated tar file to it
during the build so that the android build tools will add it to our
APK file. However, the gitkeep was not re-added when the folder moved.
This allows our WebViewImplementationNative class to paint into an
Android Bitmap class from the onDraw() view event. We also set the
viewport geometry from the view since we're setting the Kotlin Bitmap
at the same time.
This ports over the `LADYBIRD_USE_LLD` option from the standalone
Ladybird build and generalizes it to work for mold as well: the
`LAGOM_USE_LINKER` variable can be set to the desired name of the
linker. If it's empty, we default to trying LLD and Mold on ELF
platforms (in this order).
This template app from Android Studio should hopefully be more fun to
work on than the Qt wrapped application we were using before. :^)
It currently builds the native code using gradle rules, and has a stub
WebViewImplementationNative class that will wrap a c++ class of the same
name that inhertis from WebView::ViewImplementation. Spawning helper
processes and creating proper views in Kotlin is next on the list.
These classes are used as-is in all chromes. Move them to LibWebView so
that non-Serenity chromes don't have to awkwardly reach into its headers
and sources.
This adds menu items to open an interactive JavaScript console for a web
page. This more or less mimics the Qt implementation of the console.
Hooks are included to tie the lifetime of the console window with the
tab it belongs to; if the tab is closed, the console window is closed.
This creates WebView::ConsoleClient to handle functionality that will be
common to the JS consoles of all Ladybird chromes. This will let each
chrome focus on just the UI.
Note that this includes the `console.group` functionality that only the
Serenity chrome previously had. This was a FIXME in the Qt chrome, and
it is implemented such that all chromes will receive this functionality
for free.
The LadybirdWebView currently assumed it is viewed with a Tab instance.
This will not be true with the JavaScript console. This patch removes
this assumption by plumbing WebContent callbacks through a new protocol.
The Tab interface then implements this protocol.
For example, the JavaScript console will invoke window.scrollTo(0, INF)
to scroll to the bottom of the console after updating its contents. The
NSScrollView being scrolled here seems to behave oddly if we scroll
beyond its limit (e.g. mouse events stop working). Prevent this by
limiting scrolling to the NSScrollView's document rect.
We were relying on TabController setting the web view's initial viewport
in [TabController windowDidResize], which has been triggered when the
Tab is first created. However, it turns out that only happens due to the
toolbar being added to the Tab window, causing a resize event. When the
web view is embedded in a window without a toolbar, this resize event is
not triggered. Therefore, we must set the viewport ourselves when the
web view is added to a window.
Currently, the only NSWindow type in the AppKit chrome is the Tab. Once
we have other window types (e.g. Inspector), commands which assume they
are used on a Tab will either crash or behave weirdly.
This changes the createNewTab: command to accept the tab from which the
new tab is created, rather than assuming that tab is the key window. So
if some JS on a page calls window.open() while a non-Tab window is key,
the new tab will be opened within the same tab group.
This also changes closeCurrentTab: to work on any key window. Regardless
of whether the key window is a Tab or some other window, pressing cmd+W
should just close that window.
This lets the user choose a color scheme which differs from the active
system theme. Upon changing the color scheme, the scheme is broadcast to
all active tabs, and will be used in new tabs.
Without setting the --resources flag, headless-browser defaults to /res
for all resources it tries to find, including the theme. It will not
find this path on Lagom, so our attempt to load the default theme does
not accomplish anything.
The old names sounded like awkward English to me, as they implied the
WebContent process is asking for information. In reality, WebContent is
*providing* the information.
We can easily add hooks to notify the browsers of these events if any
implementation-specific handling is needed in the future, but for now,
these only repaint the client, which we can do in ViewImplementation.
Storing the backup bitmap is the same across Browser and Ladybird. Just
peform that work in LibWebView, and handle only the implementation-
specific nuances within the browsers.
This also sets the default callback to do what every non-Serenity
browser is doing, rather than copy-pasting this callback into every
implementation. The callback is still available for any platform which
might want to override the default behavior. For example, OOPWV now
overrides this callback to use FileSystemAccessClient.
This adds an alternative Ladybird chrome for macOS using the AppKit
framework. Just about everything needed for normal web browsing has
been implemented. This includes:
* Tabbed, scrollable navigation
* History navigation (back, forward, reload)
* Keyboard / mouse events
* Favicons
* Context menus
* Cookies
* Dialogs (alert, confirm, prompt)
* WebDriver support
This does not include debugging tools like the JavaScript console and
inspector, nor theme support.
The Qt chrome is still used by default. To use the AppKit chrome, set
the ENABLE_QT CMake option to OFF.
By running ctest -C Integration -R WPT, you can run WPT. Adding just
-C Integration will run all other tests *and* the WPT test.
Also add support for ./Meta/serenity.sh test lagom WPT to serenity.sh
The error.html page now uses the resource_directory_url this
variable contains the relative path to /Base/res/ on the host
system as a file:// url. This is needed for future pages to load
resource files like icons. For the error.html page this was not
really needed because it lies over this own URL in FrameLoader.cpp.
Non-Qt chromes will want to use the same Info.plist and bundle info as
the Qt chrome. This patch puts the CMake setup for the bundle into a
function that non-Qt chromes may call in their CMakeLists.txt. The Qt
chrome calls it automatically.
This will allow us to bring the WebContent process into non-Qt macOS
chromes. This branch is only reached when creating an <audio> element,
so while the chrome is heavily under development, we can just avoid
these elements.
We were still missing the resources and the libraries inside the actual
bundle directory. Do it at install time to not make a mess of all the
rules. The gn build lists all the libraries in a massive list, which
is quite a pain. We can over-copy a few libraries like this to make the
install script a bit easier to follow.
We weren't installing a lot of generated sources for the top level Lagom
build or for LibWeb, making it impossible to use LibWeb from a
find_package. ...And also Kernel/API/KeyCode.h, which is included by
no less than 8 different files in Userland/Libraries. We also weren't
installing any Ladybird header files.
We already do this for headless-browser. There's no need to open any URL
other than about:blank when starting a WebDriver session. We should also
do this from WebDriver code, rather than in special logic in Browser's
main.cpp.
The issue noted in the removed comment no longer seems to apply. The URL
loaded here is triggered before WebDriver even issues the POST /session
command.
When we launch Ladybird, we currently:
1. Create a BrowserWindow, whose constructor navigates to the configured
new tab page URL.
2. Then navigate to either "about:blank" or whatever URL is provided via
the command line.
This patch removes step 2 and forwards the URL from the command line (if
any) to BrowserWindow. BrowserWindow's constructor then either navigates
to that URL or the new tab page URL.
Currently, if the JS console is open and tied to the last opened tab in
the browser window, it will prevent the main process from exiting when
the last tab is closed. This change explicitly closes that tab before
closing the window (if it's the last tab), allowing Qt to delete the
Tab object.
This will help a lot with developing chromes for different UI frameworks
where we can see which helper classes and processes are really using Qt
vs just using it to get at helper data.
As a bonus, remove Qt dependency from WebDriver.
With the CMake build, the ladybird binary ends up in
$build/bin/ladybird.app/Contents/MacOS. Which is a bit unfortunate for
a few reasons, but the main one is that --enable-lagom-networking
doesn't work with ./Meta/serenity.sh run lagom ladybird.
Let's copy the file to the expected location. There's very likely better
solutions we can use in the future.
This object is available as `window.internals` (or just `internals`) and
is only accessible while running in "test mode".
This first version only has one API: gc(), which triggers a garbage
collection immediately.
In the future, we can add more APIs here to help us test parts of the
engine that are hard or impossible to reach via public web APIs.
The Qt docs are not that clear, but to get the number of steps the
wheel was scrolled you divide by 120 (which when multiplied by
wheelScrollLines() gives the scroll offset).
Documentation says that pixelDelta() is not reliable across platforms
so always using angleDelta() should produce more predictable scrolling
behaviour.
Rather than directing mouse wheel events to the QAbstractScrollArea,
send them to the WebContent process, allowing it to determine if they
should be consumed by the element or used for page scrolling.
The implementation of this plugin is meant to eventually replace all
current audio plugins in Ladybird. The benefits over the current Qt-
based audio playback plugin in Ladybird are:
- Low latency: With direct access to PulseAudio, we can ask for a
specific latency to output to allow minimal delay when pausing or
seeking a stream.
- Accurate timestamps: The Qt audio playback API does not expose audio
time properly. When we have access directly to PulseAudio APIs, we can
enable their timing interpolation to get an accurate monotonically-
increasing timestamp of the playing audio.
- Resiliency: With more control over how the underlying audio API is
called, we have the power to fix most bugs we might encounter. The
PulseAudio wrappers already avoid some bugs that occur with QAudioSink
when running through WSLg.
Previously, a QTimer was used to start processing of our event queue in
the main Qt event loop. Unfortunately, QTimers are not thread-safe, and
disallow starting of a timer from a different thread than it was
created in.
Instead, use a dummy QObject to post a custom QEvent to the main loop
from whatever thread we like, and process our event queue when it is
received by our dummy object.