diff --git a/Base/res/ladybird/about-pages/settings.html b/Base/res/ladybird/about-pages/settings.html index 5ec01ff8967..318dabebd58 100644 --- a/Base/res/ladybird/about-pages/settings.html +++ b/Base/res/ladybird/about-pages/settings.html @@ -377,9 +377,14 @@
Privacy
-
- - +
+
+ + +
+

Tell websites not to sell or share your data.

diff --git a/Base/res/ladybird/about-pages/settings/privacy.js b/Base/res/ladybird/about-pages/settings/privacy.js index 30d79649387..a8d4aaa8900 100644 --- a/Base/res/ladybird/about-pages/settings/privacy.js +++ b/Base/res/ladybird/about-pages/settings/privacy.js @@ -1,11 +1,11 @@ -const doNotTrackToggle = document.querySelector("#do-not-track-toggle"); +const globalPrivacyControlToggle = document.querySelector("#global-privacy-control-toggle"); function loadSettings(settings) { - doNotTrackToggle.checked = settings.doNotTrack; + globalPrivacyControlToggle.checked = settings.globalPrivacyControl; } -doNotTrackToggle.addEventListener("change", () => { - ladybird.sendMessage("setDoNotTrack", doNotTrackToggle.checked); +globalPrivacyControlToggle.addEventListener("change", () => { + ladybird.sendMessage("setGlobalPrivacyControl", globalPrivacyControlToggle.checked); }); document.addEventListener("WebUIMessage", event => { diff --git a/Libraries/LibWeb/CMakeLists.txt b/Libraries/LibWeb/CMakeLists.txt index 62ea059d048..1ec82d967c6 100644 --- a/Libraries/LibWeb/CMakeLists.txt +++ b/Libraries/LibWeb/CMakeLists.txt @@ -405,6 +405,7 @@ set(SOURCES Geometry/DOMRect.cpp Geometry/DOMRectList.cpp Geometry/DOMRectReadOnly.cpp + GPC/GlobalPrivacyControl.cpp HighResolutionTime/Performance.cpp HighResolutionTime/TimeOrigin.cpp HTML/AbstractWorker.cpp diff --git a/Libraries/LibWeb/Fetch/Fetching/Fetching.cpp b/Libraries/LibWeb/Fetch/Fetching/Fetching.cpp index 1df8e1242af..ad26ba8301c 100644 --- a/Libraries/LibWeb/Fetch/Fetching/Fetching.cpp +++ b/Libraries/LibWeb/Fetch/Fetching/Fetching.cpp @@ -1931,8 +1931,10 @@ WebIDL::ExceptionOr> http_network_or_cache_fetch(JS::Re // NOTE: `Accept` and `Accept-Language` are already included (unless fetch() is used, which does not include // the latter by default), and `Accept-Charset` is a waste of bytes. See HTTP header layer division for // more details. - if (ResourceLoader::the().enable_do_not_track() && !http_request->header_list()->contains("DNT"sv.bytes())) { - auto header = Infrastructure::Header::from_string_pair("DNT"sv, "1"sv); + // + // https://w3c.github.io/gpc/#the-sec-gpc-header-field-for-http-requests + if (ResourceLoader::the().enable_global_privacy_control() && !http_request->header_list()->contains("Sec-GPC"sv.bytes())) { + auto header = Infrastructure::Header::from_string_pair("Sec-GPC"sv, "1"sv); http_request->header_list()->append(move(header)); } diff --git a/Libraries/LibWeb/GPC/GlobalPrivacyControl.cpp b/Libraries/LibWeb/GPC/GlobalPrivacyControl.cpp new file mode 100644 index 00000000000..a06f4fb1139 --- /dev/null +++ b/Libraries/LibWeb/GPC/GlobalPrivacyControl.cpp @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2025, Tim Flynn + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include + +namespace Web::GlobalPrivacyControl { + +GlobalPrivacyControlMixin::~GlobalPrivacyControlMixin() = default; + +// https://w3c.github.io/gpc/#dom-globalprivacycontrol-globalprivacycontrol +bool GlobalPrivacyControlMixin::global_privacy_control() const +{ + // The value is false if no Sec-GPC header field would be sent; otherwise, the value is true. + return ResourceLoader::the().enable_global_privacy_control(); +} + +} diff --git a/Libraries/LibWeb/GPC/GlobalPrivacyControl.h b/Libraries/LibWeb/GPC/GlobalPrivacyControl.h new file mode 100644 index 00000000000..0ccd3280b7d --- /dev/null +++ b/Libraries/LibWeb/GPC/GlobalPrivacyControl.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2025, Tim Flynn + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +namespace Web::GlobalPrivacyControl { + +// https://w3c.github.io/gpc/#dom-globalprivacycontrol +class GlobalPrivacyControlMixin { +public: + virtual ~GlobalPrivacyControlMixin(); + + bool global_privacy_control() const; +}; + +} diff --git a/Libraries/LibWeb/GPC/GlobalPrivacyControl.idl b/Libraries/LibWeb/GPC/GlobalPrivacyControl.idl new file mode 100644 index 00000000000..c8b594dbe8e --- /dev/null +++ b/Libraries/LibWeb/GPC/GlobalPrivacyControl.idl @@ -0,0 +1,4 @@ +// https://w3c.github.io/gpc/#dom-globalprivacycontrol +interface mixin GlobalPrivacyControl { + readonly attribute boolean globalPrivacyControl; +}; diff --git a/Libraries/LibWeb/HTML/Navigator.cpp b/Libraries/LibWeb/HTML/Navigator.cpp index 293376682e2..d5c2c815f18 100644 --- a/Libraries/LibWeb/HTML/Navigator.cpp +++ b/Libraries/LibWeb/HTML/Navigator.cpp @@ -134,18 +134,6 @@ WebIDL::Long Navigator::max_touch_points() return 0; } -// https://www.w3.org/TR/tracking-dnt/#dom-navigator-donottrack -Optional Navigator::do_not_track() const -{ - // The value is null if no DNT header field would be sent (e.g., because a tracking preference is not - // enabled and no user-granted exception is applicable); otherwise, the value is a string beginning with - // "0" or "1", possibly followed by DNT-extension characters. - if (ResourceLoader::the().enable_do_not_track()) - return "1"_fly_string; - - return {}; -} - GC::Ref Navigator::service_worker() { if (!m_service_worker_container) diff --git a/Libraries/LibWeb/HTML/Navigator.h b/Libraries/LibWeb/HTML/Navigator.h index 8f34b11152f..7234cc295a0 100644 --- a/Libraries/LibWeb/HTML/Navigator.h +++ b/Libraries/LibWeb/HTML/Navigator.h @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -24,11 +25,13 @@ namespace Web::HTML { -class Navigator : public Bindings::PlatformObject +class Navigator + : public Bindings::PlatformObject , public NavigatorBeaconPartial , public NavigatorConcurrentHardwareMixin , public NavigatorDeviceMemoryMixin , public Gamepad::NavigatorGamepadPartial + , public GlobalPrivacyControl::GlobalPrivacyControlMixin , public EncryptedMediaExtensions::NavigatorEncryptedMediaExtensionsPartial , public NavigatorIDMixin , public NavigatorLanguageMixin @@ -63,8 +66,6 @@ public: [[nodiscard]] GC::Ref user_activation(); [[nodiscard]] GC::Ref credentials(); - Optional do_not_track() const; - GC::Ref service_worker(); GC::Ref media_capabilities(); diff --git a/Libraries/LibWeb/HTML/Navigator.idl b/Libraries/LibWeb/HTML/Navigator.idl index 2272bfcf13a..19217cdf382 100644 --- a/Libraries/LibWeb/HTML/Navigator.idl +++ b/Libraries/LibWeb/HTML/Navigator.idl @@ -3,6 +3,7 @@ #import #import #import +#import #import #import #import @@ -37,9 +38,6 @@ interface Navigator { // https://html.spec.whatwg.org/multipage/interaction.html#useractivation [SameObject] readonly attribute UserActivation userActivation; - // https://www.w3.org/TR/tracking-dnt/#dom-navigator-donottrack - readonly attribute DOMString? doNotTrack; - // https://w3c.github.io/ServiceWorker/#navigator-serviceworker [SecureContext, SameObject] readonly attribute ServiceWorkerContainer serviceWorker; @@ -77,6 +75,7 @@ interface mixin NavigatorAutomationInformation { readonly attribute boolean webdriver; }; +Navigator includes GlobalPrivacyControl; Navigator includes NavigatorID; Navigator includes NavigatorLanguage; Navigator includes NavigatorOnLine; diff --git a/Libraries/LibWeb/HTML/WorkerNavigator.h b/Libraries/LibWeb/HTML/WorkerNavigator.h index 70f8cc6f616..554c5a3e31f 100644 --- a/Libraries/LibWeb/HTML/WorkerNavigator.h +++ b/Libraries/LibWeb/HTML/WorkerNavigator.h @@ -8,6 +8,7 @@ #pragma once #include +#include #include #include #include @@ -20,7 +21,9 @@ namespace Web::HTML { -class WorkerNavigator : public Bindings::PlatformObject +class WorkerNavigator + : public Bindings::PlatformObject + , public GlobalPrivacyControl::GlobalPrivacyControlMixin , public NavigatorConcurrentHardwareMixin , public NavigatorDeviceMemoryMixin , public NavigatorIDMixin diff --git a/Libraries/LibWeb/HTML/WorkerNavigator.idl b/Libraries/LibWeb/HTML/WorkerNavigator.idl index 6de2556a1d9..d2867eab3ad 100644 --- a/Libraries/LibWeb/HTML/WorkerNavigator.idl +++ b/Libraries/LibWeb/HTML/WorkerNavigator.idl @@ -1,3 +1,4 @@ +#import #import #import #import @@ -20,6 +21,7 @@ interface WorkerNavigator { [SecureContext, SameObject] readonly attribute ServiceWorkerContainer serviceWorker; }; +Navigator includes GlobalPrivacyControl; WorkerNavigator includes NavigatorID; WorkerNavigator includes NavigatorLanguage; WorkerNavigator includes NavigatorOnLine; diff --git a/Libraries/LibWeb/Loader/ResourceLoader.h b/Libraries/LibWeb/Loader/ResourceLoader.h index 60d93603afa..9cd0afd8a4f 100644 --- a/Libraries/LibWeb/Loader/ResourceLoader.h +++ b/Libraries/LibWeb/Loader/ResourceLoader.h @@ -66,8 +66,8 @@ public: NavigatorCompatibilityMode navigator_compatibility_mode() { return m_navigator_compatibility_mode; } void set_navigator_compatibility_mode(NavigatorCompatibilityMode mode) { m_navigator_compatibility_mode = mode; } - bool enable_do_not_track() const { return m_enable_do_not_track; } - void set_enable_do_not_track(bool enable) { m_enable_do_not_track = enable; } + bool enable_global_privacy_control() const { return m_enable_global_privacy_control; } + void set_enable_global_privacy_control(bool enable) { m_enable_global_privacy_control = enable; } void clear_cache(); void evict_from_cache(LoadRequest const&); @@ -91,7 +91,7 @@ private: String m_platform; Vector m_preferred_languages = { "en"_string }; NavigatorCompatibilityMode m_navigator_compatibility_mode; - bool m_enable_do_not_track { false }; + bool m_enable_global_privacy_control { false }; }; } diff --git a/Libraries/LibWebView/Settings.cpp b/Libraries/LibWebView/Settings.cpp index 0b9875aeee4..ad88871ed5a 100644 --- a/Libraries/LibWebView/Settings.cpp +++ b/Libraries/LibWebView/Settings.cpp @@ -42,7 +42,7 @@ static constexpr auto site_setting_site_filters_key = "siteFilters"sv; static constexpr auto autoplay_key = "autoplay"sv; -static constexpr auto do_not_track_key = "doNotTrack"sv; +static constexpr auto global_privacy_control_key = "globalPrivacyControl"sv; static constexpr auto dns_settings_key = "dnsSettings"sv; @@ -141,8 +141,8 @@ Settings Settings::create(Badge) load_site_setting(settings.m_autoplay, autoplay_key); - if (auto do_not_track = settings_json.value().get_bool(do_not_track_key); do_not_track.has_value()) - settings.m_do_not_track = *do_not_track ? DoNotTrack::Yes : DoNotTrack::No; + if (auto global_privacy_control = settings_json.value().get_bool(global_privacy_control_key); global_privacy_control.has_value()) + settings.m_global_privacy_control = *global_privacy_control ? GlobalPrivacyControl::Yes : GlobalPrivacyControl::No; if (auto dns_settings = settings_json.value().get(dns_settings_key); dns_settings.has_value()) settings.m_dns_settings = parse_dns_settings(*dns_settings); @@ -215,7 +215,7 @@ JsonValue Settings::serialize_json() const save_site_setting(m_autoplay, autoplay_key); - settings.set(do_not_track_key, m_do_not_track == DoNotTrack::Yes); + settings.set(global_privacy_control_key, m_global_privacy_control == GlobalPrivacyControl::Yes); // dnsSettings :: { mode: "system" } | { mode: "custom", server: string, port: u16, type: "udp" | "tls", forciblyEnabled: bool, dnssec: bool } JsonObject dns_settings; @@ -253,7 +253,7 @@ void Settings::restore_defaults() m_custom_search_engines.clear(); m_autocomplete_engine.clear(); m_autoplay = SiteSetting {}; - m_do_not_track = DoNotTrack::No; + m_global_privacy_control = GlobalPrivacyControl::No; m_dns_settings = SystemDNS {}; persist_settings(); @@ -265,7 +265,7 @@ void Settings::restore_defaults() observer.search_engine_changed(); observer.autocomplete_engine_changed(); observer.autoplay_settings_changed(); - observer.do_not_track_changed(); + observer.global_privacy_control_changed(); observer.dns_settings_changed(); } } @@ -438,13 +438,13 @@ void Settings::remove_all_autoplay_site_filters() observer.autoplay_settings_changed(); } -void Settings::set_do_not_track(DoNotTrack do_not_track) +void Settings::set_global_privacy_control(GlobalPrivacyControl global_privacy_control) { - m_do_not_track = do_not_track; + m_global_privacy_control = global_privacy_control; persist_settings(); for (auto& observer : m_observers) - observer.do_not_track_changed(); + observer.global_privacy_control_changed(); } DNSSettings Settings::parse_dns_settings(JsonValue const& dns_settings) diff --git a/Libraries/LibWebView/Settings.h b/Libraries/LibWebView/Settings.h index fa4fecd3de3..460f6038115 100644 --- a/Libraries/LibWebView/Settings.h +++ b/Libraries/LibWebView/Settings.h @@ -25,7 +25,7 @@ struct WEBVIEW_API SiteSetting { OrderedHashTable site_filters; }; -enum class DoNotTrack { +enum class GlobalPrivacyControl { No, Yes, }; @@ -41,7 +41,7 @@ public: virtual void search_engine_changed() { } virtual void autocomplete_engine_changed() { } virtual void autoplay_settings_changed() { } - virtual void do_not_track_changed() { } + virtual void global_privacy_control_changed() { } virtual void dns_settings_changed() { } }; @@ -79,8 +79,8 @@ public: void remove_autoplay_site_filter(String const&); void remove_all_autoplay_site_filters(); - DoNotTrack do_not_track() const { return m_do_not_track; } - void set_do_not_track(DoNotTrack); + GlobalPrivacyControl global_privacy_control() const { return m_global_privacy_control; } + void set_global_privacy_control(GlobalPrivacyControl); static DNSSettings parse_dns_settings(JsonValue const&); DNSSettings const& dns_settings() const { return m_dns_settings; } @@ -105,7 +105,7 @@ private: Vector m_custom_search_engines; Optional m_autocomplete_engine; SiteSetting m_autoplay; - DoNotTrack m_do_not_track { DoNotTrack::No }; + GlobalPrivacyControl m_global_privacy_control { GlobalPrivacyControl::No }; DNSSettings m_dns_settings { SystemDNS() }; bool m_dns_override_by_command_line { false }; diff --git a/Libraries/LibWebView/ViewImplementation.cpp b/Libraries/LibWebView/ViewImplementation.cpp index a79e3cc2bd6..bd02967e49f 100644 --- a/Libraries/LibWebView/ViewImplementation.cpp +++ b/Libraries/LibWebView/ViewImplementation.cpp @@ -605,7 +605,7 @@ void ViewImplementation::initialize_client(CreateNewClient create_new_client) default_zoom_level_factor_changed(); languages_changed(); autoplay_settings_changed(); - do_not_track_changed(); + global_privacy_control_changed(); } void ViewImplementation::handle_web_content_process_crash(LoadErrorPage load_error_page) @@ -676,10 +676,10 @@ void ViewImplementation::autoplay_settings_changed() client().async_set_autoplay_allowlist(page_id(), autoplay_settings.site_filters.values()); } -void ViewImplementation::do_not_track_changed() +void ViewImplementation::global_privacy_control_changed() { - auto do_not_track = Application::settings().do_not_track(); - client().async_set_enable_do_not_track(page_id(), do_not_track == DoNotTrack::Yes); + auto global_privacy_control = Application::settings().global_privacy_control(); + client().async_set_enable_global_privacy_control(page_id(), global_privacy_control == GlobalPrivacyControl::Yes); } static ErrorOr save_screenshot(Gfx::Bitmap const* bitmap) diff --git a/Libraries/LibWebView/ViewImplementation.h b/Libraries/LibWebView/ViewImplementation.h index 9f32cf502e3..bf7eaa7e36c 100644 --- a/Libraries/LibWebView/ViewImplementation.h +++ b/Libraries/LibWebView/ViewImplementation.h @@ -275,7 +275,7 @@ protected: virtual void default_zoom_level_factor_changed() override; virtual void languages_changed() override; virtual void autoplay_settings_changed() override; - virtual void do_not_track_changed() override; + virtual void global_privacy_control_changed() override; void initialize_context_menus(); diff --git a/Libraries/LibWebView/WebUI/SettingsUI.cpp b/Libraries/LibWebView/WebUI/SettingsUI.cpp index 1b847404c48..65ee21eda3d 100644 --- a/Libraries/LibWebView/WebUI/SettingsUI.cpp +++ b/Libraries/LibWebView/WebUI/SettingsUI.cpp @@ -63,8 +63,8 @@ void SettingsUI::register_interfaces() remove_all_site_setting_filters(data); }); - register_interface("setDoNotTrack"sv, [this](auto const& data) { - set_do_not_track(data); + register_interface("setGlobalPrivacyControl"sv, [this](auto const& data) { + set_global_privacy_control(data); }); register_interface("setDNSSettings"sv, [this](auto const& data) { @@ -275,12 +275,12 @@ void SettingsUI::remove_all_site_setting_filters(JsonValue const& site_setting) load_current_settings(); } -void SettingsUI::set_do_not_track(JsonValue const& do_not_track) +void SettingsUI::set_global_privacy_control(JsonValue const& global_privacy_control) { - if (!do_not_track.is_bool()) + if (!global_privacy_control.is_bool()) return; - WebView::Application::settings().set_do_not_track(do_not_track.as_bool() ? DoNotTrack::Yes : DoNotTrack::No); + WebView::Application::settings().set_global_privacy_control(global_privacy_control.as_bool() ? GlobalPrivacyControl::Yes : GlobalPrivacyControl::No); } void SettingsUI::set_dns_settings(JsonValue const& dns_settings) diff --git a/Libraries/LibWebView/WebUI/SettingsUI.h b/Libraries/LibWebView/WebUI/SettingsUI.h index 6a65f8ae838..80f892ab780 100644 --- a/Libraries/LibWebView/WebUI/SettingsUI.h +++ b/Libraries/LibWebView/WebUI/SettingsUI.h @@ -36,7 +36,7 @@ private: void remove_site_setting_filter(JsonValue const&); void remove_all_site_setting_filters(JsonValue const&); - void set_do_not_track(JsonValue const&); + void set_global_privacy_control(JsonValue const&); void set_dns_settings(JsonValue const&); }; diff --git a/Services/WebContent/ConnectionFromClient.cpp b/Services/WebContent/ConnectionFromClient.cpp index 0b9769ad4c1..dffc07cc891 100644 --- a/Services/WebContent/ConnectionFromClient.cpp +++ b/Services/WebContent/ConnectionFromClient.cpp @@ -1142,9 +1142,9 @@ void ConnectionFromClient::set_preferred_languages(u64, Vector preferred Web::ResourceLoader::the().set_preferred_languages(move(preferred_languages)); } -void ConnectionFromClient::set_enable_do_not_track(u64, bool enable) +void ConnectionFromClient::set_enable_global_privacy_control(u64, bool enable) { - Web::ResourceLoader::the().set_enable_do_not_track(enable); + Web::ResourceLoader::the().set_enable_global_privacy_control(enable); } void ConnectionFromClient::set_has_focus(u64 page_id, bool has_focus) diff --git a/Services/WebContent/ConnectionFromClient.h b/Services/WebContent/ConnectionFromClient.h index a5a52857965..91f524a173d 100644 --- a/Services/WebContent/ConnectionFromClient.h +++ b/Services/WebContent/ConnectionFromClient.h @@ -110,7 +110,7 @@ private: virtual void set_preferred_contrast(u64 page_id, Web::CSS::PreferredContrast) override; virtual void set_preferred_motion(u64 page_id, Web::CSS::PreferredMotion) override; virtual void set_preferred_languages(u64 page_id, Vector) override; - virtual void set_enable_do_not_track(u64 page_id, bool) override; + virtual void set_enable_global_privacy_control(u64 page_id, bool) override; virtual void set_has_focus(u64 page_id, bool) override; virtual void set_is_scripting_enabled(u64 page_id, bool) override; virtual void set_device_pixels_per_css_pixel(u64 page_id, float) override; diff --git a/Services/WebContent/WebContentServer.ipc b/Services/WebContent/WebContentServer.ipc index e62d60c0136..a1404b609dd 100644 --- a/Services/WebContent/WebContentServer.ipc +++ b/Services/WebContent/WebContentServer.ipc @@ -96,7 +96,7 @@ endpoint WebContentServer set_preferred_contrast(u64 page_id, Web::CSS::PreferredContrast contrast) =| set_preferred_motion(u64 page_id, Web::CSS::PreferredMotion motion) =| set_preferred_languages(u64 page_id, Vector preferred_languages) =| - set_enable_do_not_track(u64 page_id, bool enable) =| + set_enable_global_privacy_control(u64 page_id, bool enable) =| set_has_focus(u64 page_id, bool has_focus) =| set_is_scripting_enabled(u64 page_id, bool is_scripting_enabled) =| set_device_pixels_per_css_pixel(u64 page_id, float device_pixels_per_css_pixel) =|