Recompile Chromium with JavaScript, Cookies, Notifications and Profiles disabled, with Claude's help

I recompiled Chromium with JavaScript, Cookies, Notifications and Profiles disabled. No cookies, no cookie banners, no tracking, no nonsense. A nicer Lynx with a GUI.

Here are the steps to follow, naturally, on Linux - in my case a Fedora based Toolbx container:

Install build dependencies:

sudo dnf install gperf flex bison clang lld llvm ninja-build nodejs npm python3 java-11-openjdk-headless libdrm-devel libxkbcommon-devel mesa-libgbm-devel pango-devel cairo-devel alsa-lib-devel cups-devel dbus-devel gtk3-devel nss-devel libXtst-devel libXdamage-devel

Clone and initialize depo_tools:

cd ~
git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
export PATH="$PATH:$HOME/depot_tools"
gclient

Clone Chromium:

mkdir chromium && cd chromium
fetch --no-history chromium

Generate build config:

cd src
gn gen out/Default --args='is_debug=false is_component_build=true symbol_level=0 blink_symbol_level=0'

Write this patch generated with Claude's Opus 4.5 help to a file named decrapify.patch:

diff --git a/chrome/browser/notifications/notification_permission_context.cc b/chrome/browser/notifications/notification_permission_context.cc
index 9bab2508a6..50ec9c8d12 100644
--- a/chrome/browser/notifications/notification_permission_context.cc
+++ b/chrome/browser/notifications/notification_permission_context.cc
@@ -143,6 +143,12 @@ ContentSetting NotificationPermissionContext::GetPermissionStatusForExtension(
 void NotificationPermissionContext::DecidePermission(
     std::unique_ptr<permissions::PermissionRequestData> request_data,
     permissions::BrowserPermissionCallback callback) {
+
+  std::move(callback).Run(content::PermissionResult(
+      blink::mojom::PermissionStatus::DENIED,
+      content::PermissionStatusSource::UNSPECIFIED));
+  return;
+
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
   // Permission requests for either Web Notifications and Push Notifications may
diff --git a/chrome/browser/translate/translate_service.cc b/chrome/browser/translate/translate_service.cc
index 0f1d12b75d..2e02cfff5f 100644
--- a/chrome/browser/translate/translate_service.cc
+++ b/chrome/browser/translate/translate_service.cc
@@ -142,14 +142,7 @@ bool TranslateService::IsTranslatableURL(const GURL& url) {
   // - about:blank
   // - Chrome OS file manager extension
   // Note: Keep in sync with condition in TranslateAgent::PageCaptured.
-  return !url.is_empty() && !url.SchemeIs(content::kChromeUIScheme) &&
-         !url.SchemeIs(chrome::kChromeNativeScheme) &&
-         !url.SchemeIs(content::kChromeDevToolsScheme) &&
-#if BUILDFLAG(IS_CHROMEOS)
-         !(url.SchemeIs(extensions::kExtensionScheme) &&
-           url.DomainIs(file_manager::kFileManagerAppId)) &&
-#endif
-         !url.IsAboutBlank();
+return false;
 }
 
 bool TranslateService::IsAvailable(PrefService* prefs) {
diff --git a/chrome/browser/ui/views/profiles/profile_picker_view.cc b/chrome/browser/ui/views/profiles/profile_picker_view.cc
index e7d1b55897..800a728c30 100644
--- a/chrome/browser/ui/views/profiles/profile_picker_view.cc
+++ b/chrome/browser/ui/views/profiles/profile_picker_view.cc
@@ -165,22 +165,7 @@ void ClearLockedProfilesFirstBrowserKeepAlive() {
 
 // static
 void ProfilePicker::Show(Params&& params) {
-  // Re-open with new params if necessary.
-  if (g_profile_picker_view && g_profile_picker_view->MaybeReopen(params)) {
-    return;
-  }
-
-  if (g_profile_picker_view) {
-    g_profile_picker_view->UpdateParams(std::move(params));
-  } else {
-    if (g_browser_process->IsShuttingDown()) {
-      // The profile picker takes a KeepAlive, which is not possible during
-      // shutdown.
-      return;
-    }
-    g_profile_picker_view = new ProfilePickerView(std::move(params));
-  }
-  g_profile_picker_view->Display();
+  return;
 }
 
 // static
diff --git a/chrome/browser/ui/views/toolbar/toolbar_view.cc b/chrome/browser/ui/views/toolbar/toolbar_view.cc
index 04636a49d0..cedf440cda 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_view.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_view.cc
@@ -480,22 +480,22 @@ void ToolbarView::Init() {
 
   avatar_ = container_view_->AddChildView(
       std::make_unique<AvatarToolbarButton>(browser_view_));
-  bool show_avatar_toolbar_button = true;
+//  bool show_avatar_toolbar_button = false;
 #if BUILDFLAG(IS_CHROMEOS)
   // ChromeOS only badges Incognito, Guest, and captive portal signin icons in
   // the browser window.
-  show_avatar_toolbar_button =
-      browser_->profile()->IsIncognitoProfile() ||
-      browser_->profile()->IsGuestSession() ||
-      (browser_->profile()->IsOffTheRecord() &&
-       browser_->profile()->GetOTRProfileID().IsCaptivePortal());
-#else
-  // DevTools profiles are OffTheRecord, so hide it there.
-  show_avatar_toolbar_button = browser_->profile()->IsIncognitoProfile() ||
-                               browser_->profile()->IsGuestSession() ||
-                               browser_->profile()->IsRegularProfile();
+//  show_avatar_toolbar_button =
+//      browser_->profile()->IsIncognitoProfile() ||
+//      browser_->profile()->IsGuestSession() ||
+//      (browser_->profile()->IsOffTheRecord() &&
+//       browser_->profile()->GetOTRProfileID().IsCaptivePortal());
+//#else
+//  // DevTools profiles are OffTheRecord, so hide it there.
+//  show_avatar_toolbar_button = browser_->profile()->IsIncognitoProfile() ||
+//                               browser_->profile()->IsGuestSession() ||
+//                               browser_->profile()->IsRegularProfile();
 #endif
-  avatar_->SetVisible(show_avatar_toolbar_button);
+  avatar_->SetVisible(false);
 
 #if BUILDFLAG(ENABLE_WEBUI_TAB_STRIP)
   auto new_tab_button = std::make_unique<ToolbarButton>(base::BindRepeating(
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 0d236c3409..9f3111708f 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -3713,7 +3713,7 @@ const blink::web_pref::WebPreferences WebContentsImpl::ComputeWebPreferences(
   if (ChildProcessSecurityPolicyImpl::GetInstance()->HasWebUIBindings(
           GetRenderViewHost()->GetProcess()->GetDeprecatedID())) {
     prefs.loads_images_automatically = true;
-    prefs.javascript_enabled = true;
+    prefs.javascript_enabled = false;
   }
 
   prefs.viewport_enabled = command_line.HasSwitch(switches::kEnableViewport);
diff --git a/extensions/browser/extension_webkit_preferences.cc b/extensions/browser/extension_webkit_preferences.cc
index ea362fb212..1feff1636c 100644
--- a/extensions/browser/extension_webkit_preferences.cc
+++ b/extensions/browser/extension_webkit_preferences.cc
@@ -36,7 +36,7 @@ void SetPreferences(const extensions::Extension* extension,
     // Extensions are trusted so we override any user preferences for disabling
     // javascript or images.
     webkit_prefs->loads_images_automatically = true;
-    webkit_prefs->javascript_enabled = true;
+    webkit_prefs->javascript_enabled = false;
 
     // Tabs aren't typically allowed to close windows. But extensions shouldn't
     // be subject to that.
diff --git a/net/cookies/cookie_monster.cc b/net/cookies/cookie_monster.cc
index fd937f5ec7..d1085f7c9f 100644
--- a/net/cookies/cookie_monster.cc
+++ b/net/cookies/cookie_monster.cc
@@ -529,6 +529,13 @@ void CookieMonster::SetCanonicalCookieAsync(
     const CookieOptions& options,
     SetCookiesCallback callback,
     std::optional<CookieAccessResult> cookie_access_result) {
+
+
+  CookieInclusionStatus status;
+  status.AddExclusionReason(CookieInclusionStatus::ExclusionReason::EXCLUDE_FAILURE_TO_STORE);
+  std::move(callback).Run(CookieAccessResult(status));
+  return;
+
   if constexpr (DCHECK_IS_ON()) {
     CanonicalCookie::CanonicalizationResult result = cookie->IsCanonical();
     DCHECK(result) << result;
diff --git a/third_party/blink/renderer/core/frame/local_frame.cc b/third_party/blink/renderer/core/frame/local_frame.cc
index 7e54411c71..883a93c3b8 100644
--- a/third_party/blink/renderer/core/frame/local_frame.cc
+++ b/third_party/blink/renderer/core/frame/local_frame.cc
@@ -4148,6 +4148,7 @@ bool LocalFrame::ImagesEnabled() {
 }
 
 bool LocalFrame::ScriptEnabled() {
+  return false;
   DCHECK(!IsDetached());
   // If this is called in the middle of detach, GetDocumentLoader() might
   // already be nullptr.

Apply the patch:

git apply --check decrapify.patch

Compile:

autoninja -C out/Default chrome

It will take a while. Until then have a quick look at the performance of my water cooled 7950x under load:

Open the browser:

out/Default/chrome

DevTools will be disabled as that feature is JavaScript based. Enjoy a clean and safe Internet.