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 @@
-
-
-
+
+
+
+
+
+
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) =|