Hide denies geolocation entirely; Manual reports the coordinates of a country you pick, so a page's navigator.geolocation answers with a plausible position instead of your real one.
-
-
These reduce tracking and hide your IP, but a custom browser can still be fingerprinted. For maximum anonymity, use the Tor Browser.
@@ -935,7 +890,19 @@
}
const refreshAll = () => refresh().then(refreshExtra, refreshExtra);
el("torOn").addEventListener("change", () => { if (C.toggleTor) C.toggleTor().catch(() => {}); setTimeout(refreshExtra, 1200); setTimeout(refreshExtra, 4000); });
- el("vpnRow").addEventListener("click", () => C.openPanel && C.openPanel("vpn:main"));
+ // VPN row: only offer it when the VPN add-on is installed AND its panel
+ // registered. Otherwise clicking it used to open whichever add-on was
+ // first in the sidebar list (usually Aegis). Hide it in that case.
+ (async () => {
+ const vpnRow = el("vpnRow");
+ try {
+ const list = await (C.listAddons ? C.listAddons() : Promise.resolve({}));
+ const panels = (list && list.sidebarPanels) || [];
+ const vpn = panels.find((p) => p.panelId === "vpn:main");
+ if (!vpn) { vpnRow.hidden = true; return; }
+ vpnRow.addEventListener("click", () => C.openPanel && C.openPanel("vpn:main"));
+ } catch { vpnRow.hidden = true; }
+ })();
document.addEventListener("section", (e) => { if (String(e.detail).startsWith("privacy")) refreshAll(); });
refreshAll();
})();
@@ -973,13 +940,18 @@
// same languageMode/languageValue setting the Anti-fingerprinting Language
// row edits. "Auto" = languageMode="show" (follow OS); anything else
// switches to languageMode="manual" and pins languageValue.
+ // Same list the toolbar globe menu uses (kept in sync with
+ // WEBSITE_LANGUAGE_QUICK in main.js). Language name in its own script;
+ // the BCP-47 tag only surfaces as the 2-letter chip in the URL bar once
+ // picked, never in the picker. English is the UK original (en-GB) —
+ // the US variant is 90%+ the same text and shared "EN" anyway.
const WEB_LANG_QUICK = [
- { tag: "en-US", label: "English (US)" }, { tag: "en-GB", label: "English (UK)" },
+ { tag: "en-GB", label: "English" },
{ tag: "es-ES", label: "Español (España)" }, { tag: "es-MX", label: "Español (México)" },
{ tag: "pt-BR", label: "Português (Brasil)" }, { tag: "pt-PT", label: "Português (Portugal)" },
{ tag: "fr-FR", label: "Français" }, { tag: "de-DE", label: "Deutsch" },
{ tag: "it-IT", label: "Italiano" }, { tag: "nl-NL", label: "Nederlands" },
- { tag: "ru-RU", label: "Русский" }, { tag: "uk-UA", label: "Українська" },
+ { tag: "ru-RU", label: "Русский" },
{ tag: "pl-PL", label: "Polski" }, { tag: "cs-CZ", label: "Čeština" },
{ tag: "sv-SE", label: "Svenska" }, { tag: "fi-FI", label: "Suomi" },
{ tag: "tr-TR", label: "Türkçe" }, { tag: "el-GR", label: "Ελληνικά" },
@@ -989,6 +961,13 @@
{ tag: "ko-KR", label: "한국어" }, { tag: "vi-VN", label: "Tiếng Việt" },
{ tag: "th-TH", label: "ไทย" }, { tag: "id-ID", label: "Bahasa Indonesia" },
];
+ // "English" for a locale tag ("en-US" → "English", "de-DE" → "German") via
+ // browser Intl. Used for the "Automatic (…)" label so it reads as a
+ // language name rather than a raw tag.
+ const langNameFor = (tag) => {
+ try { return new Intl.DisplayNames(["en"], { type: "language" }).of(tag) || tag; }
+ catch { return tag; }
+ };
(async () => {
const webLangPick = document.getElementById("webLangPick");
const webLangOther = document.getElementById("webLangOther");
@@ -997,8 +976,10 @@
let osLoc = "en-US";
try { osLoc = (C.systemLocale && await C.systemLocale()) || "en-US"; } catch {}
// Build options: "Automatic" first, then the quick list, then "Other…".
- const opts = [``,
- ...WEB_LANG_QUICK.map((L) => ``),
+ // No BCP-47 tag in visible labels — the tag surfaces only as the 2-letter
+ // chip in the URL bar.
+ const opts = [``,
+ ...WEB_LANG_QUICK.map((L) => ``),
``];
webLangPick.innerHTML = opts.join("");
const paint = (cur) => {
@@ -1006,15 +987,15 @@
if (mode !== "manual") {
webLangPick.value = "__auto__"; webLangOther.hidden = true;
hint.innerHTML = mode === "show"
- ? "Following your operating system. Also switchable from the globe icon in the address bar."
+ ? `Following your operating system (${langNameFor(osLoc)}). Also switchable from the globe icon in the address bar.`
: `Anti-fingerprinting is currently overriding this (mode: ${mode}). Change it under Privacy → Anti-fingerprinting.`;
return;
}
- const tag = cur.languageValue || "en-US";
+ const tag = cur.languageValue || "en-GB";
const known = new Set(WEB_LANG_QUICK.map((L) => L.tag));
if (known.has(tag)) { webLangPick.value = tag; webLangOther.hidden = true; }
else { webLangPick.value = "__other__"; webLangOther.hidden = false; webLangOther.value = tag; }
- hint.textContent = `Websites see you in ${tag}. Also switchable from the globe icon in the address bar.`;
+ hint.textContent = `Websites see you in ${langNameFor(tag)}. Also switchable from the globe icon in the address bar.`;
};
paint(s);
webLangPick.addEventListener("change", () => {
@@ -1310,68 +1291,116 @@
lngMode.value = s.languageMode || "show";
applyLng();
lngMode.addEventListener("change", () => { C.set("languageMode", lngMode.value); applyLng(); });
- // location: region dropdown when spoofing, city dropdown when manual.
- // Manual writes lat/lon into settings via the CITIES map below; the raw
- // coord inputs were dropped from the UI (nobody types them by hand).
- const locMode = document.getElementById("locationMode");
- const locRegion = document.getElementById("locationRegion");
- const locCity = document.getElementById("locationCity");
- locRegion.value = s.locationRegion || "europe";
- locRegion.addEventListener("change", () => C.set("locationRegion", locRegion.value));
- // Location manual reveals the city select; picking "Other…" reveals a
- // lat/lon pair, whose values are pushed to locationLat/Lon settings.
- const locOther = document.getElementById("locationOther");
- const locLatOther = document.getElementById("locationLatOther");
- const locLonOther = document.getElementById("locationLonOther");
- const applyLoc = () => {
- locRegion.hidden = locMode.value !== "spoof";
- locCity.hidden = locMode.value !== "manual";
- locOther.hidden = locMode.value !== "manual" || locCity.value !== "__other__";
+ // Location: three modes — Show real / Hide / Manual (pick a country).
+ // Manual writes the country's representative lat/lon into locationLat +
+ // locationLon, which is what navigator.geolocation returns to pages via
+ // the override in main.js. Legacy `spoof` (region) and free-form city
+ // picks are gone from the UI; effLocation() in main still handles a
+ // saved `spoof` mode so old profiles keep working until they change it.
+ // COUNTRIES: ISO-3166 alpha-2 → [ label, lat, lon ]. Coord is the
+ // country's capital (or biggest city) so pages that geocode the position
+ // land in the right country. Alphabetised for the dropdown.
+ const COUNTRIES = {
+ AR: ["Argentina", -34.6037, -58.3816],
+ AT: ["Austria", 48.2082, 16.3738],
+ AU: ["Australia", -33.8688, 151.2093],
+ BE: ["Belgium", 50.8503, 4.3517],
+ BR: ["Brazil", -23.5505, -46.6333],
+ CA: ["Canada", 43.6532, -79.3832],
+ CH: ["Switzerland", 47.3769, 8.5417],
+ CL: ["Chile", -33.4489, -70.6693],
+ CN: ["China", 31.2304, 121.4737],
+ CO: ["Colombia", 4.7110, -74.0721],
+ CZ: ["Czechia", 50.0755, 14.4378],
+ DE: ["Germany", 52.5200, 13.4050],
+ DK: ["Denmark", 55.6761, 12.5683],
+ EG: ["Egypt", 30.0444, 31.2357],
+ ES: ["Spain", 40.4168, -3.7038],
+ FI: ["Finland", 60.1699, 24.9384],
+ FR: ["France", 48.8566, 2.3522],
+ GB: ["United Kingdom", 51.5074, -0.1278],
+ GR: ["Greece", 37.9838, 23.7275],
+ HK: ["Hong Kong", 22.3193, 114.1694],
+ ID: ["Indonesia", -6.2088, 106.8456],
+ IE: ["Ireland", 53.3498, -6.2603],
+ IL: ["Israel", 32.0853, 34.7818],
+ IN: ["India", 19.0760, 72.8777],
+ IT: ["Italy", 41.9028, 12.4964],
+ JP: ["Japan", 35.6762, 139.6503],
+ KE: ["Kenya", -1.2921, 36.8219],
+ KR: ["South Korea", 37.5665, 126.9780],
+ MA: ["Morocco", 33.5731, -7.5898],
+ MX: ["Mexico", 19.4326, -99.1332],
+ MY: ["Malaysia", 3.1390, 101.6869],
+ NG: ["Nigeria", 6.5244, 3.3792],
+ NL: ["Netherlands", 52.3676, 4.9041],
+ NO: ["Norway", 59.9139, 10.7522],
+ NZ: ["New Zealand", -36.8485, 174.7633],
+ PE: ["Peru", -12.0464, -77.0428],
+ PH: ["Philippines", 14.5995, 120.9842],
+ PL: ["Poland", 52.2297, 21.0122],
+ PT: ["Portugal", 38.7223, -9.1393],
+ RU: ["Russia", 55.7558, 37.6173],
+ SA: ["Saudi Arabia", 24.7136, 46.6753],
+ SE: ["Sweden", 59.3293, 18.0686],
+ SG: ["Singapore", 1.3521, 103.8198],
+ TH: ["Thailand", 13.7563, 100.5018],
+ TR: ["Turkey", 41.0082, 28.9784],
+ TW: ["Taiwan", 25.0330, 121.5654],
+ UA: ["Ukraine", 50.4501, 30.5234],
+ US: ["United States", 40.7128, -74.0060],
+ VN: ["Vietnam", 10.8231, 106.6297],
+ ZA: ["South Africa", -26.2041, 28.0473],
};
- locMode.value = s.locationMode || "show";
+ // Legacy region → representative country (Spoof mode is retired but
+ // migrating old profiles keeps them roughly where they were).
+ const REGION_TO_COUNTRY = {
+ europe: "DE", asia: "JP", north_america: "US", south_america: "BR",
+ africa: "KE", middle_east: "AE", australia: "AU",
+ };
+ const locMode = document.getElementById("locationMode");
+ const locCountry = document.getElementById("locationCountry");
+ // Build the country dropdown from the map above.
+ locCountry.innerHTML = Object.entries(COUNTRIES)
+ .sort((a, b) => a[1][0].localeCompare(b[1][0]))
+ .map(([code, [name]]) => ``)
+ .join("");
+ // Restore prior selection: prefer an explicit locationCountry; otherwise
+ // recover the country whose coords match the saved lat/lon; else Germany.
+ const initialCountry = (() => {
+ if (s.locationCountry && COUNTRIES[s.locationCountry]) return s.locationCountry;
+ const la = Number(s.locationLat), lo = Number(s.locationLon);
+ for (const [code, [, x, y]] of Object.entries(COUNTRIES)) {
+ if (Math.abs(x - la) < 0.05 && Math.abs(y - lo) < 0.05) return code;
+ }
+ return "DE";
+ })();
+ locCountry.value = initialCountry;
+ // Migrate legacy spoof/region on this render pass so the mode dropdown
+ // has a valid value and the settings file is normalised on next save.
+ const initialMode = (() => {
+ if (s.locationMode === "spoof") {
+ const code = REGION_TO_COUNTRY[s.locationRegion] || "DE";
+ const [, la, lo] = COUNTRIES[code];
+ C.set("locationCountry", code);
+ C.set("locationLat", String(la));
+ C.set("locationLon", String(lo));
+ C.set("locationMode", "manual");
+ locCountry.value = code;
+ return "manual";
+ }
+ return s.locationMode || "show";
+ })();
+ const applyLoc = () => { locCountry.hidden = locMode.value !== "manual"; };
+ locMode.value = initialMode;
applyLoc();
locMode.addEventListener("change", () => { C.set("locationMode", locMode.value); applyLoc(); });
- locCity.addEventListener("change", applyLoc);
- // Prefill Other lat/lon inputs from saved settings.
- locLatOther.value = s.locationLat ?? "";
- locLonOther.value = s.locationLon ?? "";
- const saveOtherCoords = () => {
- const la = String(locLatOther.value).trim();
- const lo = String(locLonOther.value).trim();
- if (la !== "") C.set("locationLat", la);
- if (lo !== "") C.set("locationLon", lo);
- C.set("locationCity", "__other__");
- };
- locLatOther.addEventListener("change", saveOtherCoords);
- locLonOther.addEventListener("change", saveOtherCoords);
- // If the previously-saved city was "__other__", set the select to it so the
- // Other inputs stay visible on reload.
- if (s.locationCity === "__other__") locCity.value = "__other__";
- // Picking a city writes its lat/lon to settings — that's what
- // navigator.geolocation returns to pages once "manual" mode is active.
- const CITIES = {
- london:[51.5074,-0.1278], berlin:[52.52,13.405], paris:[48.8566,2.3522],
- madrid:[40.4168,-3.7038], rome:[41.9028,12.4964], moscow:[55.7558,37.6173],
- istanbul:[41.0082,28.9784], dubai:[25.2048,55.2708], mumbai:[19.076,72.8777],
- singapore:[1.3521,103.8198], bangkok:[13.7563,100.5018], shanghai:[31.2304,121.4737],
- tokyo:[35.6762,139.6503], seoul:[37.5665,126.978], sydney:[-33.8688,151.2093],
- new_york:[40.7128,-74.006], los_angeles:[34.0522,-118.2437], chicago:[41.8781,-87.6298],
- toronto:[43.6532,-79.3832], mexico_city:[19.4326,-99.1332], sao_paulo:[-23.5505,-46.6333],
- buenos_aires:[-34.6037,-58.3816], cairo:[30.0444,31.2357], nairobi:[-1.2921,36.8219],
- johannesburg:[-26.2041,28.0473],
- };
- // Restore prior city selection when possible by matching stored lat/lon.
- const restoreCity = () => {
- const la = Number(s.locationLat), lo = Number(s.locationLon);
- for (const [key, [x, y]] of Object.entries(CITIES))
- if (Math.abs(x - la) < 0.01 && Math.abs(y - lo) < 0.01) { locCity.value = key; return; }
- };
- restoreCity();
- locCity.addEventListener("change", () => {
- const c = CITIES[locCity.value]; if (!c) return;
- C.set("locationLat", String(c[0]));
- C.set("locationLon", String(c[1]));
- C.set("locationCity", locCity.value); // save the pick so we can restore it later
+ locCountry.addEventListener("change", () => {
+ const code = locCountry.value;
+ const c = COUNTRIES[code]; if (!c) return;
+ C.set("locationCountry", code);
+ C.set("locationLat", String(c[1]));
+ C.set("locationLon", String(c[2]));
});
// Storage: "Clear all now" wipes everything the toggles cover, without