diff --git a/Base/res/ladybird/about-pages/settings.html b/Base/res/ladybird/about-pages/settings.html new file mode 100644 index 0000000000..b482f26616 --- /dev/null +++ b/Base/res/ladybird/about-pages/settings.html @@ -0,0 +1,254 @@ + + + + Settings + + + + +
+ + + + +

Ladybird Settings

+
+ +
+
General
+
+
+ + +
+
+
+ +
+
Search
+
+
+
+ + +
+
+ +
+
+ +
+ +
+ + + + diff --git a/Libraries/LibWeb/CMakeLists.txt b/Libraries/LibWeb/CMakeLists.txt index c8e3c1a53e..d452403eaf 100644 --- a/Libraries/LibWeb/CMakeLists.txt +++ b/Libraries/LibWeb/CMakeLists.txt @@ -564,6 +564,7 @@ set(SOURCES Internals/Internals.cpp Internals/InternalsBase.cpp Internals/Processes.cpp + Internals/Settings.cpp IntersectionObserver/IntersectionObserver.cpp IntersectionObserver/IntersectionObserverEntry.cpp Layout/AudioBox.cpp diff --git a/Libraries/LibWeb/HTML/Window.cpp b/Libraries/LibWeb/HTML/Window.cpp index 231c5138f2..2fe1d0b5a4 100644 --- a/Libraries/LibWeb/HTML/Window.cpp +++ b/Libraries/LibWeb/HTML/Window.cpp @@ -61,6 +61,7 @@ #include #include #include +#include #include #include #include @@ -738,9 +739,10 @@ WebIDL::ExceptionOr Window::initialize_web_interfaces(Badge(realm), JS::default_attributes); - } + else if (path == "settings"sv) + define_direct_property("settings", realm.create(realm), JS::default_attributes); } return {}; diff --git a/Libraries/LibWeb/Internals/Settings.cpp b/Libraries/LibWeb/Internals/Settings.cpp new file mode 100644 index 0000000000..d3237f55d1 --- /dev/null +++ b/Libraries/LibWeb/Internals/Settings.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2025, Tim Flynn + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include + +namespace Web::Internals { + +GC_DEFINE_ALLOCATOR(Settings); + +Settings::Settings(JS::Realm& realm) + : InternalsBase(realm) +{ +} + +Settings::~Settings() = default; + +void Settings::initialize(JS::Realm& realm) +{ + Base::initialize(realm); + WEB_SET_PROTOTYPE_FOR_INTERFACE(Settings); +} + +void Settings::load_current_settings() +{ + page().client().request_current_settings(); +} + +void Settings::restore_default_settings() +{ + page().client().restore_default_settings(); +} + +void Settings::set_new_tab_page_url(String const& new_tab_page_url) +{ + if (auto parsed_new_tab_page_url = URL::Parser::basic_parse(new_tab_page_url); parsed_new_tab_page_url.has_value()) + page().client().set_new_tab_page_url(*parsed_new_tab_page_url); +} + +void Settings::load_available_search_engines() +{ + page().client().request_available_search_engines(); +} + +void Settings::set_search_engine(Optional const& search_engine) +{ + page().client().set_search_engine(search_engine); +} + +} diff --git a/Libraries/LibWeb/Internals/Settings.h b/Libraries/LibWeb/Internals/Settings.h new file mode 100644 index 0000000000..169d940dcc --- /dev/null +++ b/Libraries/LibWeb/Internals/Settings.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2025, Tim Flynn + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +namespace Web::Internals { + +class Settings final : public InternalsBase { + WEB_PLATFORM_OBJECT(Settings, InternalsBase); + GC_DECLARE_ALLOCATOR(Settings); + +public: + virtual ~Settings() override; + + void load_current_settings(); + void restore_default_settings(); + + void set_new_tab_page_url(String const& new_tab_page_url); + + void load_available_search_engines(); + void set_search_engine(Optional const& search_engine); + +private: + explicit Settings(JS::Realm&); + + virtual void initialize(JS::Realm&) override; +}; + +} diff --git a/Libraries/LibWeb/Internals/Settings.idl b/Libraries/LibWeb/Internals/Settings.idl new file mode 100644 index 0000000000..da584c72fd --- /dev/null +++ b/Libraries/LibWeb/Internals/Settings.idl @@ -0,0 +1,10 @@ +[Exposed=Nobody] +interface Settings { + undefined loadCurrentSettings(); + undefined restoreDefaultSettings(); + + undefined setNewTabPageURL(USVString newTabPageURL); + + undefined loadAvailableSearchEngines(); + undefined setSearchEngine(DOMString? search_engine); +}; diff --git a/Libraries/LibWeb/Page/Page.h b/Libraries/LibWeb/Page/Page.h index 5abb1043cf..e845ff43cc 100644 --- a/Libraries/LibWeb/Page/Page.h +++ b/Libraries/LibWeb/Page/Page.h @@ -402,6 +402,12 @@ public: virtual void update_process_statistics() { } + virtual void request_current_settings() { } + virtual void restore_default_settings() { } + virtual void set_new_tab_page_url(URL::URL const&) { } + virtual void request_available_search_engines() { } + virtual void set_search_engine(Optional const&) { } + virtual bool is_ready_to_paint() const = 0; virtual DisplayListPlayerType display_list_player_type() const = 0; diff --git a/Libraries/LibWeb/idl_files.cmake b/Libraries/LibWeb/idl_files.cmake index b51b98cd33..6962eb5a47 100644 --- a/Libraries/LibWeb/idl_files.cmake +++ b/Libraries/LibWeb/idl_files.cmake @@ -267,6 +267,7 @@ libweb_js_bindings(IndexedDB/IDBVersionChangeEvent) libweb_js_bindings(Internals/InternalAnimationTimeline) libweb_js_bindings(Internals/Internals) libweb_js_bindings(Internals/Processes) +libweb_js_bindings(Internals/Settings) libweb_js_bindings(IntersectionObserver/IntersectionObserver) libweb_js_bindings(IntersectionObserver/IntersectionObserverEntry) libweb_js_bindings(MathML/MathMLElement) diff --git a/Libraries/LibWebView/Application.cpp b/Libraries/LibWebView/Application.cpp index 5f3e7ab682..3634ec2e91 100644 --- a/Libraries/LibWebView/Application.cpp +++ b/Libraries/LibWebView/Application.cpp @@ -5,6 +5,7 @@ */ #include +#include #include #include #include @@ -336,6 +337,35 @@ void Application::send_updated_process_statistics_to_view(ViewImplementation& vi view.run_javascript(MUST(builder.to_string())); } +void Application::send_current_settings_to_view(ViewImplementation& view) +{ + auto settings = m_settings.serialize_json(); + + StringBuilder builder; + builder.append("settings.loadSettings(\""sv); + builder.append_escaped_for_json(settings); + builder.append("\");"sv); + + view.run_javascript(MUST(builder.to_string())); +} + +void Application::send_available_search_engines_to_view(ViewImplementation& view) +{ + StringBuilder engines; + + auto serializer = MUST(JsonArraySerializer<>::try_create(engines)); + for (auto const& engine : search_engines()) + MUST(serializer.add(engine.name)); + MUST(serializer.finish()); + + StringBuilder builder; + builder.append("settings.loadSearchEngines(\""sv); + builder.append_escaped_for_json(engines.string_view()); + builder.append("\");"sv); + + view.run_javascript(MUST(builder.to_string())); +} + void Application::process_did_exit(Process&& process) { if (m_in_shutdown) diff --git a/Libraries/LibWebView/Application.h b/Libraries/LibWebView/Application.h index db73ecb4bd..04522eb257 100644 --- a/Libraries/LibWebView/Application.h +++ b/Libraries/LibWebView/Application.h @@ -61,6 +61,9 @@ public: void send_updated_process_statistics_to_view(ViewImplementation&); + void send_current_settings_to_view(ViewImplementation&); + void send_available_search_engines_to_view(ViewImplementation&); + ErrorOr path_for_downloaded_file(StringView file) const; enum class DevtoolsState { diff --git a/Libraries/LibWebView/WebContentClient.cpp b/Libraries/LibWebView/WebContentClient.cpp index feef3aee2f..cd7d0e7bac 100644 --- a/Libraries/LibWebView/WebContentClient.cpp +++ b/Libraries/LibWebView/WebContentClient.cpp @@ -670,6 +670,39 @@ void WebContentClient::update_process_statistics(u64 page_id) WebView::Application::the().send_updated_process_statistics_to_view(*view); } +void WebContentClient::request_current_settings(u64 page_id) +{ + if (auto view = view_for_page_id(page_id); view.has_value()) + WebView::Application::the().send_current_settings_to_view(*view); +} + +void WebContentClient::restore_default_settings(u64 page_id) +{ + WebView::Application::settings().restore_defaults(); + request_current_settings(page_id); +} + +void WebContentClient::set_new_tab_page_url(u64 page_id, URL::URL new_tab_page_url) +{ + WebView::Application::settings().set_new_tab_page_url(move(new_tab_page_url)); + request_current_settings(page_id); +} + +void WebContentClient::request_available_search_engines(u64 page_id) +{ + if (auto view = view_for_page_id(page_id); view.has_value()) + WebView::Application::the().send_available_search_engines_to_view(*view); +} + +void WebContentClient::set_search_engine(u64 page_id, Optional search_engine) +{ + WebView::Application::settings().set_search_engine(search_engine.map([](auto const& search_engine) { + return search_engine.bytes_as_string_view(); + })); + + request_current_settings(page_id); +} + Optional WebContentClient::view_for_page_id(u64 page_id, SourceLocation location) { // Don't bother logging anything for the spare WebContent process. It will only receive a load notification for about:blank. diff --git a/Libraries/LibWebView/WebContentClient.h b/Libraries/LibWebView/WebContentClient.h index 205f433c2e..fcb738385c 100644 --- a/Libraries/LibWebView/WebContentClient.h +++ b/Libraries/LibWebView/WebContentClient.h @@ -130,6 +130,11 @@ private: virtual void did_allocate_backing_stores(u64 page_id, i32 front_bitmap_id, Gfx::ShareableBitmap, i32 back_bitmap_id, Gfx::ShareableBitmap) override; virtual Messages::WebContentClient::RequestWorkerAgentResponse request_worker_agent(u64 page_id) override; virtual void update_process_statistics(u64 page_id) override; + virtual void request_current_settings(u64 page_id) override; + virtual void restore_default_settings(u64 page_id) override; + virtual void set_new_tab_page_url(u64 page_id, URL::URL new_tab_page_url) override; + virtual void request_available_search_engines(u64 page_id) override; + virtual void set_search_engine(u64 page_id, Optional search_engine) override; Optional view_for_page_id(u64, SourceLocation = SourceLocation::current()); diff --git a/Services/WebContent/PageClient.cpp b/Services/WebContent/PageClient.cpp index eed200cdd5..044c979a9e 100644 --- a/Services/WebContent/PageClient.cpp +++ b/Services/WebContent/PageClient.cpp @@ -704,6 +704,31 @@ void PageClient::update_process_statistics() client().async_update_process_statistics(m_id); } +void PageClient::request_current_settings() +{ + client().async_request_current_settings(m_id); +} + +void PageClient::restore_default_settings() +{ + client().async_restore_default_settings(m_id); +} + +void PageClient::set_new_tab_page_url(URL::URL const& new_tab_page_url) +{ + client().async_set_new_tab_page_url(m_id, new_tab_page_url); +} + +void PageClient::request_available_search_engines() +{ + client().async_request_available_search_engines(m_id); +} + +void PageClient::set_search_engine(Optional const& search_engine) +{ + client().async_set_search_engine(m_id, search_engine); +} + ErrorOr PageClient::connect_to_webdriver(ByteString const& webdriver_ipc_path) { VERIFY(!m_webdriver); diff --git a/Services/WebContent/PageClient.h b/Services/WebContent/PageClient.h index b04811f4d2..decad41550 100644 --- a/Services/WebContent/PageClient.h +++ b/Services/WebContent/PageClient.h @@ -175,6 +175,11 @@ private: virtual IPC::File request_worker_agent() override; virtual void page_did_mutate_dom(FlyString const& type, Web::DOM::Node const& target, Web::DOM::NodeList& added_nodes, Web::DOM::NodeList& removed_nodes, GC::Ptr previous_sibling, GC::Ptr next_sibling, Optional const& attribute_name) override; virtual void update_process_statistics() override; + virtual void request_current_settings() override; + virtual void restore_default_settings() override; + virtual void set_new_tab_page_url(URL::URL const&) override; + virtual void request_available_search_engines() override; + virtual void set_search_engine(Optional const&) override; Web::Layout::Viewport* layout_root(); void setup_palette(); diff --git a/Services/WebContent/WebContentClient.ipc b/Services/WebContent/WebContentClient.ipc index adfc6bbf2f..09963a08c6 100644 --- a/Services/WebContent/WebContentClient.ipc +++ b/Services/WebContent/WebContentClient.ipc @@ -111,4 +111,10 @@ endpoint WebContentClient request_worker_agent(u64 page_id) => (IPC::File socket) // FIXME: Add required attributes to select a SharedWorker Agent update_process_statistics(u64 page_id) =| + + request_current_settings(u64 page_id) =| + restore_default_settings(u64 page_id) =| + set_new_tab_page_url(u64 page_id, URL::URL new_tab_page_url) =| + request_available_search_engines(u64 page_id) =| + set_search_engine(u64 page_id, Optional search_engine) =| } diff --git a/UI/cmake/ResourceFiles.cmake b/UI/cmake/ResourceFiles.cmake index 64a64e6727..2ea86e801d 100644 --- a/UI/cmake/ResourceFiles.cmake +++ b/UI/cmake/ResourceFiles.cmake @@ -70,6 +70,7 @@ set(ABOUT_PAGES about.html newtab.html processes.html + settings.html ) list(TRANSFORM ABOUT_PAGES PREPEND "${LADYBIRD_SOURCE_DIR}/Base/res/ladybird/about-pages/")