2024-04-12 15:15:09 -06:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2024, Andrew Kaster <akaster@serenityos.org>
|
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
|
|
*/
|
|
|
|
|
2024-07-30 14:01:05 -04:00
|
|
|
#include <LibCore/ArgsParser.h>
|
2025-06-06 15:42:12 -04:00
|
|
|
#include <LibWebView/EventLoop/EventLoopImplementationQt.h>
|
2024-04-12 15:15:09 -06:00
|
|
|
#include <LibWebView/URL.h>
|
2024-11-09 12:50:33 -05:00
|
|
|
#include <UI/Qt/Application.h>
|
|
|
|
#include <UI/Qt/Settings.h>
|
|
|
|
#include <UI/Qt/StringUtils.h>
|
|
|
|
|
2025-09-18 08:16:36 -04:00
|
|
|
#include <QClipboard>
|
2025-09-04 08:53:57 -04:00
|
|
|
#include <QDesktopServices>
|
2024-08-28 15:32:30 -04:00
|
|
|
#include <QFileDialog>
|
2024-04-12 15:15:09 -06:00
|
|
|
#include <QFileOpenEvent>
|
2025-09-04 08:53:57 -04:00
|
|
|
#include <QMessageBox>
|
2025-09-18 08:16:36 -04:00
|
|
|
#include <QMimeData>
|
2024-04-12 15:15:09 -06:00
|
|
|
|
2025-09-16 18:06:00 -07:00
|
|
|
#if defined(AK_OS_WINDOWS)
|
|
|
|
# include <AK/Windows.h>
|
|
|
|
# include <LibCore/TimeZoneWatcher.h>
|
|
|
|
# include <QAbstractNativeEventFilter>
|
|
|
|
#endif
|
|
|
|
|
2024-04-12 15:15:09 -06:00
|
|
|
namespace Ladybird {
|
|
|
|
|
2025-09-16 18:06:00 -07:00
|
|
|
#if defined(AK_OS_WINDOWS)
|
|
|
|
class NativeWindowsTimeChangeEventFilter : public QAbstractNativeEventFilter {
|
|
|
|
public:
|
|
|
|
NativeWindowsTimeChangeEventFilter(Core::TimeZoneWatcher& time_zone_watcher)
|
|
|
|
: m_time_zone_watcher(time_zone_watcher)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
bool nativeEventFilter(QByteArray const& event_type, void* message, qintptr*) override
|
|
|
|
{
|
|
|
|
if (event_type == QByteArrayLiteral("windows_generic_MSG")) {
|
|
|
|
auto msg = static_cast<MSG*>(message);
|
|
|
|
if (msg->message == WM_TIMECHANGE) {
|
|
|
|
m_time_zone_watcher.on_time_zone_changed();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
Core::TimeZoneWatcher& m_time_zone_watcher;
|
|
|
|
};
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2025-06-10 14:56:24 -04:00
|
|
|
class LadybirdQApplication : public QApplication {
|
|
|
|
public:
|
|
|
|
explicit LadybirdQApplication(Main::Arguments& arguments)
|
|
|
|
: QApplication(arguments.argc, arguments.argv)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual bool event(QEvent* event) override
|
|
|
|
{
|
|
|
|
auto& application = static_cast<Application&>(WebView::Application::the());
|
|
|
|
|
2025-09-16 18:06:00 -07:00
|
|
|
#if defined(AK_OS_WINDOWS)
|
|
|
|
static Optional<NativeWindowsTimeChangeEventFilter> time_change_event_filter {};
|
|
|
|
if (auto time_zone_watcher = application.time_zone_watcher(); !time_change_event_filter.has_value() && time_zone_watcher.has_value()) {
|
|
|
|
time_change_event_filter.emplace(time_zone_watcher.value());
|
|
|
|
installNativeEventFilter(&time_change_event_filter.value());
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2025-06-10 14:56:24 -04:00
|
|
|
switch (event->type()) {
|
|
|
|
case QEvent::FileOpen: {
|
|
|
|
if (!application.on_open_file)
|
|
|
|
break;
|
|
|
|
|
|
|
|
auto const& open_event = *static_cast<QFileOpenEvent const*>(event);
|
|
|
|
auto file = ak_string_from_qstring(open_event.file());
|
|
|
|
|
|
|
|
if (auto file_url = WebView::sanitize_url(file); file_url.has_value())
|
|
|
|
application.on_open_file(file_url.release_value());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return QApplication::event(event);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2025-06-10 19:13:03 -04:00
|
|
|
Application::Application() = default;
|
2025-06-06 15:42:12 -04:00
|
|
|
Application::~Application() = default;
|
|
|
|
|
2025-03-15 16:56:52 -04:00
|
|
|
void Application::create_platform_options(WebView::BrowserOptions&, WebView::WebContentOptions& web_content_options)
|
2024-07-30 14:01:05 -04:00
|
|
|
{
|
|
|
|
web_content_options.config_path = Settings::the()->directory();
|
|
|
|
}
|
|
|
|
|
2025-06-06 15:42:12 -04:00
|
|
|
NonnullOwnPtr<Core::EventLoop> Application::create_platform_event_loop()
|
|
|
|
{
|
2025-06-10 14:56:24 -04:00
|
|
|
if (!browser_options().headless_mode.has_value()) {
|
|
|
|
Core::EventLoopManager::install(*new WebView::EventLoopManagerQt);
|
2025-06-11 07:32:37 -04:00
|
|
|
m_application = make<LadybirdQApplication>(arguments());
|
2025-06-10 14:56:24 -04:00
|
|
|
}
|
|
|
|
|
2025-06-06 15:42:12 -04:00
|
|
|
auto event_loop = WebView::Application::create_platform_event_loop();
|
|
|
|
|
|
|
|
if (!browser_options().headless_mode.has_value())
|
|
|
|
static_cast<WebView::EventLoopImplementationQt&>(event_loop->impl()).set_main_loop();
|
|
|
|
|
|
|
|
return event_loop;
|
|
|
|
}
|
2024-04-26 18:53:55 -06:00
|
|
|
|
2024-09-05 18:19:51 -04:00
|
|
|
BrowserWindow& Application::new_window(Vector<URL::URL> const& initial_urls, BrowserWindow::IsPopupWindow is_popup_window, Tab* parent_tab, Optional<u64> page_index)
|
2024-04-26 18:53:55 -06:00
|
|
|
{
|
2024-09-05 18:19:51 -04:00
|
|
|
auto* window = new BrowserWindow(initial_urls, is_popup_window, parent_tab, move(page_index));
|
2024-04-26 18:53:55 -06:00
|
|
|
set_active_window(*window);
|
|
|
|
window->show();
|
2024-06-20 19:11:19 +01:00
|
|
|
if (initial_urls.is_empty()) {
|
|
|
|
auto* tab = window->current_tab();
|
|
|
|
if (tab) {
|
|
|
|
tab->set_url_is_hidden(true);
|
|
|
|
tab->focus_location_editor();
|
|
|
|
}
|
|
|
|
}
|
2024-04-26 18:53:55 -06:00
|
|
|
window->activateWindow();
|
|
|
|
window->raise();
|
|
|
|
return *window;
|
|
|
|
}
|
|
|
|
|
2025-09-04 08:53:57 -04:00
|
|
|
Optional<WebView::ViewImplementation&> Application::active_web_view() const
|
|
|
|
{
|
|
|
|
if (auto* active_tab = this->active_tab())
|
|
|
|
return active_tab->view();
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2025-09-13 09:08:24 -04:00
|
|
|
Optional<WebView::ViewImplementation&> Application::open_blank_new_tab(Web::HTML::ActivateTab activate_tab) const
|
|
|
|
{
|
|
|
|
auto& tab = active_window().create_new_tab(activate_tab);
|
|
|
|
return tab.view();
|
|
|
|
}
|
|
|
|
|
2024-08-28 15:32:30 -04:00
|
|
|
Optional<ByteString> Application::ask_user_for_download_folder() const
|
|
|
|
{
|
|
|
|
auto path = QFileDialog::getExistingDirectory(nullptr, "Select download directory", QDir::homePath());
|
|
|
|
if (path.isNull())
|
|
|
|
return {};
|
|
|
|
|
|
|
|
return ak_byte_string_from_qstring(path);
|
|
|
|
}
|
|
|
|
|
2025-09-04 08:53:57 -04:00
|
|
|
void Application::display_download_confirmation_dialog(StringView download_name, LexicalPath const& path) const
|
|
|
|
{
|
|
|
|
auto message = MUST(String::formatted("{} saved to: {}", download_name, path));
|
|
|
|
|
|
|
|
QMessageBox dialog(active_tab());
|
|
|
|
dialog.setWindowTitle("Ladybird");
|
|
|
|
dialog.setIcon(QMessageBox::Information);
|
|
|
|
dialog.setText(qstring_from_ak_string(message));
|
|
|
|
dialog.addButton(QMessageBox::Ok);
|
|
|
|
dialog.addButton(QMessageBox::Open)->setText("Open folder");
|
|
|
|
|
|
|
|
if (dialog.exec() == QMessageBox::Open) {
|
|
|
|
auto path_url = QUrl::fromLocalFile(qstring_from_ak_string(path.dirname()));
|
|
|
|
QDesktopServices::openUrl(path_url);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Application::display_error_dialog(StringView error_message) const
|
|
|
|
{
|
|
|
|
QMessageBox::warning(active_tab(), "Ladybird", qstring_from_ak_string(error_message));
|
|
|
|
}
|
|
|
|
|
2025-09-18 09:18:54 -04:00
|
|
|
Utf16String Application::clipboard_text() const
|
2025-09-18 08:16:36 -04:00
|
|
|
{
|
2025-10-10 10:11:56 -04:00
|
|
|
if (browser_options().headless_mode.has_value())
|
|
|
|
return WebView::Application::clipboard_text();
|
|
|
|
|
2025-09-18 08:16:36 -04:00
|
|
|
auto const* clipboard = QGuiApplication::clipboard();
|
2025-09-18 09:18:54 -04:00
|
|
|
return utf16_string_from_qstring(clipboard->text());
|
2025-09-18 08:16:36 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
Vector<Web::Clipboard::SystemClipboardRepresentation> Application::clipboard_entries() const
|
|
|
|
{
|
2025-10-10 10:11:56 -04:00
|
|
|
if (browser_options().headless_mode.has_value())
|
|
|
|
return WebView::Application::clipboard_entries();
|
|
|
|
|
2025-09-18 08:16:36 -04:00
|
|
|
Vector<Web::Clipboard::SystemClipboardRepresentation> representations;
|
|
|
|
auto const* clipboard = QGuiApplication::clipboard();
|
|
|
|
|
|
|
|
auto const* mime_data = clipboard->mimeData();
|
|
|
|
if (!mime_data)
|
|
|
|
return {};
|
|
|
|
|
|
|
|
for (auto const& format : mime_data->formats()) {
|
|
|
|
auto data = ak_byte_string_from_qbytearray(mime_data->data(format));
|
|
|
|
auto mime_type = ak_string_from_qstring(format);
|
|
|
|
|
|
|
|
representations.empend(move(data), move(mime_type));
|
|
|
|
}
|
|
|
|
|
|
|
|
return representations;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Application::insert_clipboard_entry(Web::Clipboard::SystemClipboardRepresentation entry)
|
|
|
|
{
|
2025-10-10 10:11:56 -04:00
|
|
|
if (browser_options().headless_mode.has_value()) {
|
|
|
|
WebView::Application::insert_clipboard_entry(move(entry));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2025-09-18 08:16:36 -04:00
|
|
|
auto* mime_data = new QMimeData();
|
|
|
|
mime_data->setData(qstring_from_ak_string(entry.mime_type), qbytearray_from_ak_string(entry.data));
|
|
|
|
|
|
|
|
auto* clipboard = QGuiApplication::clipboard();
|
|
|
|
clipboard->setMimeData(mime_data);
|
|
|
|
}
|
|
|
|
|
2025-09-17 10:03:01 -04:00
|
|
|
void Application::on_devtools_enabled() const
|
|
|
|
{
|
|
|
|
WebView::Application::on_devtools_enabled();
|
|
|
|
|
|
|
|
if (m_active_window)
|
|
|
|
m_active_window->on_devtools_enabled();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Application::on_devtools_disabled() const
|
|
|
|
{
|
|
|
|
WebView::Application::on_devtools_disabled();
|
|
|
|
|
|
|
|
if (m_active_window)
|
|
|
|
m_active_window->on_devtools_disabled();
|
|
|
|
}
|
|
|
|
|
2024-04-12 15:15:09 -06:00
|
|
|
}
|