Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • gk/tor-browser
  • peterstory/tor-browser
  • sanketh/tor-browser
  • acat/tor-browser
  • sysrqb/tor-browser
  • boklm/tor-browser
  • dan/tor-browser
  • fabrizio/tor-browser
  • victorvw/tor-browser
  • aguestuser/tor-browser
  • WofWca/tor-browser
  • p13dz/tor-browser
  • mwolfe/tor-browser
  • tpo/applications/tor-browser
  • brade/tor-browser
  • pierov/tor-browser
  • ma1/tor-browser
  • JeremyRand/tor-browser
  • henry/tor-browser
  • msimonelli/tor-browser
  • cypherpunks1/tor-browser
  • blackZwork/tor-browser
  • starlingroot/tor-browser
  • cohosh/tor-browser
  • t-m-w/tor-browser
  • trinity-1686a/tor-browser
  • HHN/tor-browser
  • emmapeel/tor-browser
  • Achintya_Sharma/tor-browser
  • guest475646844/tor-browser
  • Mima/tor-browser
  • morgan/tor-browser
  • clairehurst/tor-browser
  • NoisyCoil/tor-browser
  • gus/tor-browser
  • Francewhoa/tor-browser
  • novialriptide/tor-browser
  • jwilde/tor-browser
  • brizental/tor-browser
  • ourhopeforfreedom/tor-browser
  • onyinyang/tor-browser
  • Noino/tor-browser
  • murmelurmel/tor-browser
43 results
Show changes
Showing
with 1281 additions and 43 deletions
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
document.addEventListener("dialogaccept", () => {
const retvals = window.arguments[0];
retvals.confirmed = true;
retvals.neverAskAgain = document.querySelector("#neverAskAgain").checked;
});
document.addEventListener("DOMContentLoaded", () => {
const { NewIdentityStrings } = window.arguments[0];
const dialog = document.querySelector("#newIdentityDialog");
dialog.querySelector("#infoTitle").textContent =
NewIdentityStrings.new_identity_prompt_title;
dialog.querySelector("#infoBody").textContent =
NewIdentityStrings.new_identity_prompt;
dialog.querySelector("#neverAskAgain").label =
NewIdentityStrings.new_identity_ask_again;
const accept = dialog.getButton("accept");
accept.label = NewIdentityStrings.new_identity_restart;
accept.classList.add("danger-button");
});
<?xml version="1.0"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<!-- based on resetProfile.xhtml -->
<?xml-stylesheet href="chrome://global/skin/global.css"?>
<?xml-stylesheet href="chrome://global/content/commonDialog.css"?>
<?xml-stylesheet href="chrome://global/skin/commonDialog.css"?>
<?xml-stylesheet href="chrome://browser/content/newIdentityDialog.css"?>
<window id="newIdentityDialogWindow"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:html="http://www.w3.org/1999/xhtml"
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
aria-describedby="infoBody">
<dialog id="newIdentityDialog"
buttons="accept,cancel"
defaultButton="accept">
<linkset>
<!-- Without this document.l10n is not initialized, and we need it for the
cancel button. -->
<html:link rel="localization" href="branding/brand.ftl"/>
</linkset>
<div xmlns="http://www.w3.org/1999/xhtml">
<div id="dialogGrid">
<div class="dialogRow" id="infoRow">
<div id="iconContainer">
<xul:image id="infoIcon"/>
</div>
<div id="infoContainer">
<xul:description id="infoTitle"/>
<xul:description id="infoBody" context="contentAreaContextMenu" noinitialfocus="true"/>
<xul:checkbox id="neverAskAgain"/>
</div>
</div>
</div>
</div>
<script src="chrome://browser/content/newIdentityDialog.js"/>
</dialog>
</window>
"use strict";
var EXPORTED_SYMBOLS = ["NewIdentityButton"];
/* globals CustomizableUI Services gFindBarInitialized gFindBar
OpenBrowserWindow PrivateBrowsingUtils XPCOMUtils
*/
XPCOMUtils.defineLazyGetter(this, "NewIdentityStrings", () => {
const brandBundle = Services.strings.createBundle(
"chrome://branding/locale/brand.properties"
);
const brandShortName = brandBundle.GetStringFromName("brandShortName");
let strings = {
new_identity: "New Identity",
new_identity_sentence_case: "New identity",
new_identity_prompt_title: "Reset your identity?",
new_identity_prompt: `${brandShortName} will close all windows and tabs. All website sessions will be lost. \nRestart ${brandShortName} now to reset your identity?`,
new_identity_restart: `Restart ${brandShortName}`,
new_identity_ask_again: "Never ask me again",
new_identity_menu_accesskey: "I",
};
let bundle = null;
try {
bundle = Services.strings.createBundle(
"chrome://browser/locale/newIdentity.properties"
);
} catch (e) {
console.warn("Could not load the New Identity strings");
}
if (bundle) {
for (const key of Object.keys(strings)) {
try {
strings[key] = bundle.GetStringFromName(key);
} catch (e) {}
}
strings.new_identity_prompt = strings.new_identity_prompt.replaceAll(
"%S",
brandShortName
);
strings.new_identity_restart = strings.new_identity_restart.replaceAll(
"%S",
brandShortName
);
}
return strings;
});
// Use a lazy getter because NewIdentityButton is declared more than once
// otherwise.
XPCOMUtils.defineLazyGetter(this, "NewIdentityButton", () => {
// Logger adapted from CustomizableUI.jsm
const logger = (() => {
const { ConsoleAPI } = ChromeUtils.import(
"resource://gre/modules/Console.jsm"
);
const consoleOptions = {
maxLogLevel: "info",
maxLogLevelPref: "browser.new_identity.log_level",
prefix: "NewIdentity",
};
return new ConsoleAPI(consoleOptions);
})();
const topics = Object.freeze({
newIdentityRequested: "new-identity-requested",
});
class NewIdentityImpl {
async run() {
this.disableAllJS();
await this.clearState();
await this.openNewWindow();
this.closeOldWindow();
this.broadcast();
}
// Disable JS (as a defense-in-depth measure)
disableAllJS() {
logger.info("Disabling JavaScript");
const enumerator = Services.wm.getEnumerator("navigator:browser");
while (enumerator.hasMoreElements()) {
const win = enumerator.getNext();
this.disableWindowJS(win);
}
}
disableWindowJS(win) {
const browsers = win.gBrowser?.browsers || [];
for (const browser of browsers) {
if (!browser) {
continue;
}
this.disableBrowserJS(browser);
try {
browser.webNavigation?.stop(browser.webNavigation.STOP_ALL);
} catch (e) {
logger.warn("Could not stop navigation", e, browser.currentURI);
}
}
}
disableBrowserJS(browser) {
if (!browser) {
return;
}
// Does the following still apply?
// Solution from: https://bugzilla.mozilla.org/show_bug.cgi?id=409737
// XXX: This kills the entire window. We need to redirect
// focus and inform the user via a lightbox.
const eventSuppressor = browser.contentWindow?.windowUtils;
if (browser.browsingContext) {
browser.browsingContext.allowJavascript = false;
}
try {
// My estimation is that this does not get the inner iframe windows,
// but that does not matter, because iframes should be destroyed
// on the next load.
// Should we log when browser.contentWindow is null?
if (browser.contentWindow) {
browser.contentWindow.name = null;
browser.contentWindow.window.name = null;
}
} catch (e) {
logger.warn("Failed to reset window.name", e);
}
eventSuppressor?.suppressEventHandling(true);
}
// Clear state
async clearState() {
logger.info("Clearing the state");
this.closeTabs();
this.clearSearchBar();
this.clearPrivateSessionHistory();
this.clearHTTPAuths();
this.clearCryptoTokens();
this.clearOCSPCache();
this.clearSecuritySettings();
this.clearImageCaches();
this.clearStorage();
this.clearPreferencesAndPermissions();
await this.clearData();
this.clearConnections();
this.clearPrivateSession();
}
clearSiteSpecificZoom() {
Services.prefs.setBoolPref(
"browser.zoom.siteSpecific",
!Services.prefs.getBoolPref("browser.zoom.siteSpecific")
);
Services.prefs.setBoolPref(
"browser.zoom.siteSpecific",
!Services.prefs.getBoolPref("browser.zoom.siteSpecific")
);
}
closeTabs() {
logger.info("Closing tabs");
if (
!Services.prefs.getBoolPref("browser.new_identity.close_newnym", true)
) {
logger.info("Not closing tabs");
return;
}
// TODO: muck around with browser.tabs.warnOnClose.. maybe..
logger.info("Closing tabs...");
const enumerator = Services.wm.getEnumerator("navigator:browser");
const windowsToClose = [];
while (enumerator.hasMoreElements()) {
const win = enumerator.getNext();
const browser = win.gBrowser;
if (!browser) {
logger.warn("No browser for possible window to close");
continue;
}
const tabsToRemove = [];
for (const b of browser.browsers) {
const tab = browser.getTabForBrowser(b);
if (tab) {
tabsToRemove.push(tab);
} else {
logger.warn("Browser has a null tab", b);
}
}
if (win == window) {
browser.addWebTab("about:blank");
} else {
// It is a bad idea to alter the window list while iterating
// over it, so add this window to an array and close it later.
windowsToClose.push(win);
}
// Close each tab except the new blank one that we created.
tabsToRemove.forEach(aTab => browser.removeTab(aTab));
}
// Close all XUL windows except this one.
logger.info("Closing windows...");
windowsToClose.forEach(aWin => aWin.close());
logger.info("Closed all tabs");
// This clears the undo tab history.
const tabs = Services.prefs.getIntPref(
"browser.sessionstore.max_tabs_undo"
);
Services.prefs.setIntPref("browser.sessionstore.max_tabs_undo", 0);
Services.prefs.setIntPref("browser.sessionstore.max_tabs_undo", tabs);
}
clearSearchBar() {
logger.info("Clearing searchbox");
// Bug #10800: Trying to clear search/find can cause exceptions
// in unknown cases. Just log for now.
try {
const searchBar = window.document.getElementById("searchbar");
if (searchBar) {
searchBar.textbox.reset();
}
} catch (e) {
logger.error("Exception on clearing search box", e);
}
try {
if (gFindBarInitialized) {
const findbox = gFindBar.getElement("findbar-textbox");
findbox.reset();
gFindBar.close();
}
} catch (e) {
logger.error("Exception on clearing find bar", e);
}
}
clearPrivateSessionHistory() {
logger.info("Emitting Private Browsing Session clear event");
Services.obs.notifyObservers(null, "browser:purge-session-history");
}
clearHTTPAuths() {
if (
!Services.prefs.getBoolPref(
"browser.new_identity.clear_http_auth",
true
)
) {
logger.info("Skipping HTTP Auths, because disabled");
return;
}
logger.info("Clearing HTTP Auths");
const auth = Cc["@mozilla.org/network/http-auth-manager;1"].getService(
Ci.nsIHttpAuthManager
);
auth.clearAll();
}
clearCryptoTokens() {
logger.info("Clearing Crypto Tokens");
// Clear all crypto auth tokens. This includes calls to PK11_LogoutAll(),
// nsNSSComponent::LogoutAuthenticatedPK11() and clearing the SSL session
// cache.
const sdr = Cc["@mozilla.org/security/sdr;1"].getService(
Ci.nsISecretDecoderRing
);
sdr.logoutAndTeardown();
}
clearOCSPCache() {
// nsNSSComponent::Observe() watches security.OCSP.enabled, which calls
// setValidationOptions(), which in turn calls setNonPkixOcspEnabled() which,
// if security.OCSP.enabled is set to 0, calls CERT_DisableOCSPChecking(),
// which calls CERT_ClearOCSPCache().
// See: https://mxr.mozilla.org/comm-esr24/source/mozilla/security/manager/ssl/src/nsNSSComponent.cpp
const ocsp = Services.prefs.getIntPref("security.OCSP.enabled");
Services.prefs.setIntPref("security.OCSP.enabled", 0);
Services.prefs.setIntPref("security.OCSP.enabled", ocsp);
}
clearSecuritySettings() {
// Clear site security settings
const sss = Cc["@mozilla.org/ssservice;1"].getService(
Ci.nsISiteSecurityService
);
sss.clearAll();
}
clearImageCaches() {
logger.info("Clearing Image Cache");
// In Firefox 18 and newer, there are two image caches: one that is used
// for regular browsing, and one that is used for private browsing.
this.clearImageCacheRB();
this.clearImageCachePB();
}
clearImageCacheRB() {
try {
const imgTools = Cc["@mozilla.org/image/tools;1"].getService(
Ci.imgITools
);
const imgCache = imgTools.getImgCacheForDocument(null);
// Evict all but chrome cache
imgCache.clearCache(false);
} catch (e) {
// FIXME: This can happen in some rare cases involving XULish image data
// in combination with our image cache isolation patch. Sure isn't
// a good thing, but it's not really a super-cookie vector either.
// We should fix it eventually.
logger.error("Exception on image cache clearing", e);
}
}
clearImageCachePB() {
const imgTools = Cc["@mozilla.org/image/tools;1"].getService(
Ci.imgITools
);
try {
// Try to clear the private browsing cache. To do so, we must locate a
// content document that is contained within a private browsing window.
let didClearPBCache = false;
const enumerator = Services.wm.getEnumerator("navigator:browser");
while (!didClearPBCache && enumerator.hasMoreElements()) {
const win = enumerator.getNext();
let browserDoc = win.document.documentElement;
if (!browserDoc.hasAttribute("privatebrowsingmode")) {
continue;
}
const tabbrowser = win.gBrowser;
if (!tabbrowser) {
continue;
}
for (const browser of tabbrowser.browsers) {
const doc = browser.contentDocument;
if (doc) {
const imgCache = imgTools.getImgCacheForDocument(doc);
// Evict all but chrome cache
imgCache.clearCache(false);
didClearPBCache = true;
break;
}
}
}
} catch (e) {
logger.error("Exception on private browsing image cache clearing", e);
}
}
clearStorage() {
logger.info("Clearing Disk and Memory Caches");
try {
Services.cache2.clear();
} catch (e) {
logger.error("Exception on cache clearing", e);
}
logger.info("Clearing Cookies and DOM Storage");
Services.cookies.removeAll();
}
clearPreferencesAndPermissions() {
logger.info("Clearing Content Preferences");
ChromeUtils.defineModuleGetter(
this,
"PrivateBrowsingUtils",
"resource://gre/modules/PrivateBrowsingUtils.jsm"
);
const pbCtxt = PrivateBrowsingUtils.privacyContextFromWindow(window);
const cps = Cc["@mozilla.org/content-pref/service;1"].getService(
Ci.nsIContentPrefService2
);
cps.removeAllDomains(pbCtxt);
this.clearSiteSpecificZoom();
logger.info("Clearing permissions");
try {
Services.perms.removeAll();
} catch (e) {
// Actually, this catch does not appear to be needed. Leaving it in for
// safety though.
logger.error("Cannot clear permissions", e);
}
logger.info("Syncing prefs");
// Force prefs to be synced to disk
Services.prefs.savePrefFile(null);
}
async clearData() {
logger.info("Calling the clearDataService");
const flags =
Services.clearData.CLEAR_ALL ^ Services.clearData.CLEAR_PASSWORDS;
return new Promise(resolve => {
Services.clearData.deleteData(flags, {
onDataDeleted(code) {
if (code !== Cr.NS_OK) {
logger.error(`Error while calling the clearDataService: ${code}`);
}
// We always resolve, because we do not want to interrupt the new
// identity procedure.
resolve();
},
});
});
}
clearConnections() {
logger.info("Closing open connections");
// Clear keep-alive
Services.obs.notifyObservers(this, "net:prune-all-connections");
}
clearPrivateSession() {
logger.info("Ending any remaining private browsing sessions.");
Services.obs.notifyObservers(null, "last-pb-context-exited");
}
// Broadcast as a hook to clear other data
broadcast() {
logger.info("Broadcasting the new identity");
Services.obs.notifyObservers({}, topics.newIdentityRequested);
}
// Window management
openNewWindow() {
logger.info("Opening a new window");
return new Promise(resolve => {
// Open a new window with the default homepage
// We could pass {private: true} but we do not because we enforce
// browser.privatebrowsing.autostart = true.
// What about users that change settings?
const win = OpenBrowserWindow();
// This mechanism to know when the new window is ready is used by
// OpenBrowserWindow itself (see its definition in browser.js).
win.addEventListener("MozAfterPaint", () => resolve(), { once: true });
});
}
closeOldWindow() {
logger.info("Closing the old window");
// Run garbage collection and cycle collection after window is gone.
// This ensures that blob URIs are forgotten.
window.addEventListener("unload", function(event) {
logger.debug("Initiating New Identity GC pass");
// Clear out potential pending sInterSliceGCTimer:
window.windowUtils.runNextCollectorTimer();
// Clear out potential pending sICCTimer:
window.windowUtils.runNextCollectorTimer();
// Schedule a garbage collection in 4000-1000ms...
window.windowUtils.garbageCollect();
// To ensure the GC runs immediately instead of 4-10s from now, we need
// to poke it at least 11 times.
// We need 5 pokes for GC, 1 poke for the interSliceGC, and 5 pokes for
// CC.
// See nsJSContext::RunNextCollectorTimer() in
// https://mxr.mozilla.org/mozilla-central/source/dom/base/nsJSEnvironment.cpp#1970.
// XXX: We might want to make our own method for immediate full GC...
for (let poke = 0; poke < 11; poke++) {
window.windowUtils.runNextCollectorTimer();
}
// And now, since the GC probably actually ran *after* the CC last time,
// run the whole thing again.
window.windowUtils.garbageCollect();
for (let poke = 0; poke < 11; poke++) {
window.windowUtils.runNextCollectorTimer();
}
logger.debug("Completed New Identity GC pass");
});
// Close the current window for added safety
window.close();
}
}
let newIdentityInProgress = false;
return {
topics,
init() {
// We first search in the DOM for the identity button. If it does not
// exist it may be in the toolbox palette. In the latter case we still
// need to initialize the button in case it is added back later through
// customization.
const button =
document.getElementById("new-identity-button") ||
window.gNavToolbox.palette.querySelector("#new-identity-button");
if (button) {
button.setAttribute("tooltiptext", NewIdentityStrings.new_identity);
// Include an equal label, shown in the overflow menu or during
// customization.
button.setAttribute("label", NewIdentityStrings.new_identity);
button.addEventListener("command", () => {
this.onCommand();
});
}
const viewCache = document.getElementById("appMenu-viewCache").content;
const appButton = viewCache.querySelector("#appMenu-new-identity");
if (appButton) {
appButton.setAttribute(
"label",
NewIdentityStrings.new_identity_sentence_case
);
appButton.addEventListener("command", () => {
this.onCommand();
});
}
const menu = document.querySelector("#menu_newIdentity");
if (menu) {
menu.setAttribute("label", NewIdentityStrings.new_identity);
menu.setAttribute(
"accesskey",
NewIdentityStrings.new_identity_menu_accesskey
);
menu.addEventListener("command", () => {
this.onCommand();
});
}
},
uninit() {},
async onCommand() {
try {
// Ignore if there's a New Identity in progress to avoid race
// conditions leading to failures (see bug 11783 for an example).
if (newIdentityInProgress) {
return;
}
newIdentityInProgress = true;
const prefConfirm = "browser.new_identity.confirm_newnym";
const shouldConfirm = Services.prefs.getBoolPref(prefConfirm, true);
if (shouldConfirm) {
const params = {
NewIdentityStrings,
confirmed: false,
neverAskAgain: false,
};
await window.gDialogBox.open(
"chrome://browser/content/newIdentityDialog.xhtml",
params
);
Services.prefs.setBoolPref(prefConfirm, !params.neverAskAgain);
if (!params.confirmed) {
return;
}
}
const impl = new NewIdentityImpl();
await impl.run();
} catch (e) {
// If something went wrong make sure we have the New Identity button
// enabled (again).
logger.error("Unexpected error", e);
window.alert("New Identity unexpected error: " + e);
} finally {
newIdentityInProgress = false;
}
},
};
});
browser.jar:
content/browser/newidentity.js (content/newidentity.js)
content/browser/newIdentityDialog.xhtml (content/newIdentityDialog.xhtml)
content/browser/newIdentityDialog.css (content/newIdentityDialog.css)
content/browser/newIdentityDialog.js (content/newIdentityDialog.js)
JAR_MANIFESTS += ["jar.mn"]
......@@ -435,20 +435,7 @@ class BaseAboutNewTabService {
* the newtab page has no effect on the result of this function.
*/
get defaultURL() {
// Generate the desired activity stream resource depending on state, e.g.,
// "resource://activity-stream/prerendered/activity-stream.html"
// "resource://activity-stream/prerendered/activity-stream-debug.html"
// "resource://activity-stream/prerendered/activity-stream-noscripts.html"
return [
"resource://activity-stream/prerendered/",
"activity-stream",
// Debug version loads dev scripts but noscripts separately loads scripts
this.activityStreamDebug && !this.privilegedAboutProcessEnabled
? "-debug"
: "",
this.privilegedAboutProcessEnabled ? "-noscripts" : "",
".html",
].join("");
return "about:blank";
}
get welcomeURL() {
......
......@@ -33,7 +33,6 @@
class="check-home-page-controlled"
data-preference-related="browser.startup.homepage">
<menupopup>
<menuitem value="0" data-l10n-id="home-mode-choice-default-fx" />
<menuitem value="2" data-l10n-id="home-mode-choice-custom" />
<menuitem value="1" data-l10n-id="home-mode-choice-blank" />
</menupopup>
......@@ -84,7 +83,6 @@
Preferences so we need to handle setting the pref manually.-->
<menulist id="newTabMode" flex="1" data-preference-related="browser.newtabpage.enabled">
<menupopup>
<menuitem value="0" data-l10n-id="home-mode-choice-default-fx" />
<menuitem value="1" data-l10n-id="home-mode-choice-blank" />
</menupopup>
</menulist>
......
......@@ -383,10 +383,14 @@ var gHomePane = {
if (controllingExtension && controllingExtension.id) {
newValue = controllingExtension.id;
} else if (isDefault) {
newValue = this.HOME_MODE_FIREFOX_HOME;
} else if (isBlank) {
// For base-browser, we want to check isBlank first since the default page
// is also the blank page, but we only have a menu option for
// HOME_MODE_BLANK, rather than HOME_MODE_FIREFOX_HOME.
// See tor-browser#41609.
newValue = this.HOME_MODE_BLANK;
} else if (isDefault) {
newValue = this.HOME_MODE_FIREFOX_HOME;
} else {
newValue = this.HOME_MODE_CUSTOM;
}
......
......@@ -313,7 +313,7 @@
</groupbox>
<!-- Languages -->
<groupbox id="languagesGroup" data-category="paneGeneral" hidden="true">
<groupbox id="languagesGroup" data-category="paneGeneral" hidden="true" data-subcategory="language">
<label><html:h2 data-l10n-id="language-header"/></label>
<vbox id="browserLanguagesBox" align="start" hidden="true">
......
......@@ -553,16 +553,10 @@ var gMainPane = {
.setAttribute("style", "display: none !important");
}
// Initialize the Firefox Updates section.
let version = AppConstants.MOZ_APP_VERSION_DISPLAY;
let version = AppConstants.BASE_BROWSER_VERSION;
// Include the build ID if this is an "a#" (nightly) build
if (/a\d+$/.test(version)) {
let buildID = Services.appinfo.appBuildID;
let year = buildID.slice(0, 4);
let month = buildID.slice(4, 6);
let day = buildID.slice(6, 8);
version += ` (${year}-${month}-${day})`;
}
// Base Browser and derivatives: do not include the build ID in our alphas,
// since they are not actually related to the build date.
// Append "(32-bit)" or "(64-bit)" build architecture to the version number:
let bundle = Services.strings.createBundle(
......@@ -1445,8 +1439,28 @@ var gMainPane = {
available,
{ preferNative: true }
);
let locales = available.map((code, i) => ({ code, name: localeNames[i] }));
locales.sort((a, b) => a.name > b.name);
let locales = available.map((code, i) => {
let name = localeNames[i].replace(/\s*\(.+\)$/g, "");
if (code === "ja-JP-macos") {
// Mozilla codebases handle Japanese in macOS in different ways,
// sometimes they call it ja-JP-mac and sometimes they call it
// ja-JP-macos. The former is translated to Japanese when specifying
// preferNative to true, the latter is not. Since seeing ja-JP-macos
// would be confusing anyway, we treat it as a special case.
// See tor-browser#41372 and Bug 1726586.
name =
Services.intl.getLocaleDisplayNames(undefined, ["ja"], {
preferNative: true,
})[0] + " (ja)";
} else {
name += ` (${code})`;
}
return {
code,
name,
};
});
locales.sort((a, b) => a.code.localeCompare(b.code));
let fragment = document.createDocumentFragment();
for (let { code, name } of locales) {
......
......@@ -487,6 +487,17 @@ async function scrollAndHighlight(subcategory, category) {
}
let header = getClosestDisplayedHeader(element);
// We assign a tabindex=-1 to the element so that we can focus it. This allows
// us to move screen reader's focus to an arbitrary position on the page.
// See tor-browser#41454 and bug 1799153.
element.setAttribute("tabindex", "-1");
// The element is not always immediately focusable, so we wait until the next
// loop.
setTimeout(() => {
Services.focus.setFocus(element, Services.focus.FLAG_NOSCROLL);
element.removeAttribute("tabindex");
});
scrollContentTo(header);
element.classList.add("spotlight");
}
......
......@@ -12,6 +12,7 @@
<?xml-stylesheet href="chrome://browser/skin/preferences/search.css"?>
<?xml-stylesheet href="chrome://browser/skin/preferences/containers.css"?>
<?xml-stylesheet href="chrome://browser/skin/preferences/privacy.css"?>
<?xml-stylesheet href="chrome://browser/content/securitylevel/securityLevelPreferences.css"?>
<!DOCTYPE html>
......
......@@ -1075,6 +1075,8 @@
<html:h1 data-l10n-id="security-header"/>
</hbox>
#include ../securitylevel/content/securityLevelPreferences.inc.xhtml
<!-- addons, forgery (phishing) UI Security -->
<groupbox id="browsingProtectionGroup" data-category="panePrivacy" hidden="true">
<label><html:h2 data-l10n-id="security-browsing-protection"/></label>
......
......@@ -60,6 +60,13 @@ XPCOMUtils.defineLazyGetter(this, "AlertsServiceDND", function() {
}
});
// TODO: module import via ChromeUtils.defineModuleGetter
XPCOMUtils.defineLazyScriptGetter(
this,
["SecurityLevelPreferences"],
"chrome://browser/content/securitylevel/securityLevel.js"
);
XPCOMUtils.defineLazyPreferenceGetter(
this,
"OS_AUTH_ENABLED",
......@@ -311,6 +318,16 @@ function initTCPStandardSection() {
var gPrivacyPane = {
_pane: null,
/**
* Show the Security Level UI
*/
_initSecurityLevel() {
SecurityLevelPreferences.init();
window.addEventListener("unload", () => SecurityLevelPreferences.uninit(), {
once: true,
});
},
/**
* Whether the prompt to restart Firefox should appear when changing the autostart pref.
*/
......@@ -852,6 +869,7 @@ var gPrivacyPane = {
this.trackingProtectionReadPrefs();
this.networkCookieBehaviorReadPrefs();
this._initTrackingProtectionExtensionControl();
this._initSecurityLevel();
Services.telemetry.setEventRecordingEnabled("pwmgr", true);
......
......@@ -82,7 +82,7 @@ async function test_dynamical_window_rounding(aWindow, aCheckFunc) {
// We need to wait for the updating the margins for the newly opened tab, or
// it will affect the following tests.
let promiseForTheFirstRounding = TestUtils.topicObserved(
"test:letterboxing:update-margin-finish"
"test:letterboxing:update-size-finish"
);
info("Open a content tab for testing.");
......@@ -108,7 +108,7 @@ async function test_dynamical_window_rounding(aWindow, aCheckFunc) {
let caseString = "Case " + width + "x" + height + ": ";
// Create a promise for waiting for the margin update.
let promiseRounding = TestUtils.topicObserved(
"test:letterboxing:update-margin-finish"
"test:letterboxing:update-size-finish"
);
let { containerWidth, containerHeight } = getContainerSize(tab);
......@@ -316,7 +316,7 @@ async function test_findbar(aWindow) {
);
let promiseRounding = TestUtils.topicObserved(
"test:letterboxing:update-margin-finish"
"test:letterboxing:update-size-finish"
);
let findBarOpenPromise = BrowserTestUtils.waitForEvent(
......@@ -330,7 +330,7 @@ async function test_findbar(aWindow) {
ok(true, "Margin updated when findbar opened");
promiseRounding = TestUtils.topicObserved(
"test:letterboxing:update-margin-finish"
"test:letterboxing:update-size-finish"
);
let findBarClosePromise = BrowserTestUtils.waitForEvent(
......
......@@ -8,7 +8,6 @@ const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
BrowserSearchTelemetry: "resource:///modules/BrowserSearchTelemetry.sys.mjs",
RemoteSettings: "resource://services-settings/remote-settings.sys.mjs",
SearchUtils: "resource://gre/modules/SearchUtils.sys.mjs",
});
......@@ -19,8 +18,6 @@ const SEARCH_AD_CLICKS_SCALAR_BASE = "browser.search.adclicks.";
const SEARCH_DATA_TRANSFERRED_SCALAR = "browser.search.data_transferred";
const SEARCH_TELEMETRY_PRIVATE_BROWSING_KEY_SUFFIX = "pb";
const TELEMETRY_SETTINGS_KEY = "search-telemetry-v2";
const impressionIdsWithoutEngagementsSet = new Set();
XPCOMUtils.defineLazyGetter(lazy, "logConsole", () => {
......@@ -155,13 +152,7 @@ class TelemetryHandler {
return;
}
this._telemetrySettings = lazy.RemoteSettings(TELEMETRY_SETTINGS_KEY);
let rawProviderInfo = [];
try {
rawProviderInfo = await this._telemetrySettings.get();
} catch (ex) {
lazy.logConsole.error("Could not get settings:", ex);
}
// Send the provider info to the child handler.
this._contentHandler.init(rawProviderInfo);
......
"use strict";
/* global AppConstants, Services, openPreferences, XPCOMUtils */
XPCOMUtils.defineLazyGetter(this, "SecurityLevelStrings", () => {
let strings = {
// Generic terms
security_level: "Security Level",
security_level_standard: "Standard",
security_level_safer: "Safer",
security_level_safest: "Safest",
security_level_tooltip_standard: "Security Level: Standard",
security_level_tooltip_safer: "Security Level: Safer",
security_level_tooltip_safest: "Security Level: Safest",
// Shown only for custom level
security_level_custom: "Custom",
security_level_restore: "Restore Defaults",
security_level_learn_more: "Learn more",
// Panel
security_level_open_settings: "Settings…",
security_level_standard_summary:
"All browser and website features are enabled.",
security_level_safer_summary:
"Disables website features that are often dangerous, causing some sites to lose functionality.",
security_level_safest_summary:
"Only allows website features required for static sites and basic services. These changes affect images, media, and scripts.",
security_level_custom_heading: "Custom security level configured",
security_level_custom_summary:
"Your custom browser preferences have resulted in unusual security settings. For security and privacy reasons, we recommend you choose one of the default security levels.",
// Security level section in about:preferences#privacy
security_level_overview:
"Disable certain web features that can be used to attack your security and anonymity.",
security_level_list_safer: "At the safer setting:",
security_level_list_safest: "At the safest setting:",
// Strings for descriptions
security_level_js_https_only: "JavaScript is disabled on non-HTTPS sites.",
security_level_js_disabled:
"JavaScript is disabled by default on all sites.",
security_level_limit_typography:
"Some fonts and math symbols are disabled.",
security_level_limit_typography_svg:
"Some fonts, icons, math symbols, and images are disabled.",
security_level_limit_media:
"Audio and video (HTML5 media), and WebGL are click-to-play.",
};
let bundle = null;
try {
bundle = Services.strings.createBundle(
"chrome://browser/locale/securityLevel.properties"
);
} catch (e) {
console.warn("Could not load the Security Level strings");
}
if (bundle) {
for (const key of Object.keys(strings)) {
try {
strings[key] = bundle.GetStringFromName(key);
} catch (e) {}
}
}
return strings;
});
/*
Security Level Prefs
Getters and Setters for relevant torbutton prefs
*/
var SecurityLevelPrefs = {
SecurityLevels: Object.freeze({
safest: 1,
safer: 2,
standard: 4,
}),
security_slider_pref: "browser.security_level.security_slider",
security_custom_pref: "browser.security_level.security_custom",
get securityLevel() {
// Set the default return value to 0, which won't match anything in
// SecurityLevels.
const val = Services.prefs.getIntPref(this.security_slider_pref, 0);
return Object.entries(this.SecurityLevels).find(
entry => entry[1] === val
)?.[0];
},
set securityLevel(level) {
const val = this.SecurityLevels[level];
if (val !== undefined) {
Services.prefs.setIntPref(this.security_slider_pref, val);
}
},
get securityCustom() {
return Services.prefs.getBoolPref(this.security_custom_pref);
},
set securityCustom(val) {
Services.prefs.setBoolPref(this.security_custom_pref, val);
},
}; /* Security Level Prefs */
/*
Security Level Button Code
Controls init and update of the security level toolbar button
*/
var SecurityLevelButton = {
_securityPrefsBranch: null,
/**
* Whether we have added popup listeners to the panel.
*
* @type {boolean}
*/
_panelPopupListenersSetup: false,
/**
* The toolbar button element.
*
* @type {Element}
*/
_button: null,
/**
* The button that the panel should point to. Either the toolbar button or the
* overflow button.
*
* @type {Element}
*/
_anchorButton: null,
_configUIFromPrefs() {
const level = SecurityLevelPrefs.securityLevel;
if (!level) {
return;
}
const customStr = SecurityLevelPrefs.securityCustom ? "_custom" : "";
this._button.setAttribute("level", `${level}${customStr}`);
this._button.setAttribute(
"tooltiptext",
SecurityLevelStrings[`security_level_tooltip_${level}`]
);
},
/**
* Open the panel popup for the button.
*/
openPopup() {
let anchorNode;
const overflowPanel = document.getElementById("widget-overflow");
if (overflowPanel.contains(this._button)) {
// We are in the overflow panel.
// We first close the overflow panel, otherwise focus will not return to
// the nav-bar-overflow-button if the security level panel is closed with
// "Escape" (the navigation toolbar does not track focus when a panel is
// opened whilst another is already open).
// NOTE: In principle, using PanelMultiView would allow us to open panels
// from within another panel. However, when using panelmultiview for the
// security level panel, tab navigation was broken within the security
// level panel. PanelMultiView may be set up to work with a menu-like
// panel rather than our dialog-like panel.
overflowPanel.hidePopup();
this._anchorButton = document.getElementById("nav-bar-overflow-button");
anchorNode = this._anchorButton.icon;
} else {
this._anchorButton = this._button;
anchorNode = this._button.badgeStack;
}
const panel = SecurityLevelPanel.panel;
if (!this._panelPopupListenersSetup) {
this._panelPopupListenersSetup = true;
// NOTE: We expect the _anchorButton to not change whilst the popup is
// open.
panel.addEventListener("popupshown", () => {
this._anchorButton.setAttribute("open", "true");
});
panel.addEventListener("popuphidden", () => {
this._anchorButton.removeAttribute("open");
});
}
panel.openPopup(anchorNode, "bottomcenter topright", 0, 0, false);
},
init() {
// We first search in the DOM for the security level button. If it does not
// exist it may be in the toolbox palette. We still want to return the
// button in the latter case to allow it to be initialized or adjusted in
// case it is added back through customization.
this._button =
document.getElementById("security-level-button") ||
window.gNavToolbox.palette.querySelector("#security-level-button");
// Set a label to be be used as the accessible name, and to be shown in the
// overflow menu and during customization.
this._button.setAttribute("label", SecurityLevelStrings.security_level);
this._button.addEventListener("command", () => this.openPopup());
// set the initial class based off of the current pref
this._configUIFromPrefs();
this._securityPrefsBranch = Services.prefs.getBranch(
"browser.security_level."
);
this._securityPrefsBranch.addObserver("", this);
SecurityLevelPanel.init();
},
uninit() {
this._securityPrefsBranch.removeObserver("", this);
this._securityPrefsBranch = null;
SecurityLevelPanel.uninit();
},
observe(subject, topic, data) {
switch (topic) {
case "nsPref:changed":
if (data === "security_slider" || data === "security_custom") {
this._configUIFromPrefs();
}
break;
}
},
}; /* SecurityLevelButton */
/*
Security Level Panel Code
Controls init and update of the panel in the security level hanger
*/
var SecurityLevelPanel = {
_securityPrefsBranch: null,
_populated: false,
_populateXUL() {
this._elements = {
panel: document.getElementById("securityLevel-panel"),
background: document.getElementById("securityLevel-background"),
levelName: document.getElementById("securityLevel-level"),
customName: document.getElementById("securityLevel-custom"),
summary: document.getElementById("securityLevel-summary"),
restoreDefaultsButton: document.getElementById(
"securityLevel-restoreDefaults"
),
settingsButton: document.getElementById("securityLevel-settings"),
};
document.getElementById("securityLevel-header").textContent =
SecurityLevelStrings.security_level;
this._elements.customName.textContent =
SecurityLevelStrings.security_level_custom;
const learnMoreEl = document.getElementById("securityLevel-learnMore");
learnMoreEl.textContent = SecurityLevelStrings.security_level_learn_more;
learnMoreEl.addEventListener("click", event => {
window.openTrustedLinkIn(learnMoreEl.href, "tab");
this.hide();
event.preventDefault();
});
this._elements.restoreDefaultsButton.textContent =
SecurityLevelStrings.security_level_restore;
this._elements.settingsButton.textContent =
SecurityLevelStrings.security_level_open_settings;
this._elements.restoreDefaultsButton.addEventListener("command", () => {
this.restoreDefaults();
});
this._elements.settingsButton.addEventListener("command", () => {
this.openSecuritySettings();
});
this._elements.panel.addEventListener("popupshown", () => {
// Bring focus into the panel by focusing the default button.
this._elements.panel.querySelector('button[default="true"]').focus();
});
this._populated = true;
this._configUIFromPrefs();
},
_configUIFromPrefs() {
if (!this._populated) {
console.warn("_configUIFromPrefs before XUL was populated.");
return;
}
// get security prefs
const level = SecurityLevelPrefs.securityLevel;
const custom = SecurityLevelPrefs.securityCustom;
// only visible when user is using custom settings
this._elements.customName.hidden = !custom;
this._elements.restoreDefaultsButton.hidden = !custom;
if (custom) {
this._elements.settingsButton.removeAttribute("default");
this._elements.restoreDefaultsButton.setAttribute("default", "true");
} else {
this._elements.settingsButton.setAttribute("default", "true");
this._elements.restoreDefaultsButton.removeAttribute("default");
}
// Descriptions change based on security level
this._elements.background.setAttribute("level", level);
this._elements.levelName.textContent =
SecurityLevelStrings[`security_level_${level}`];
this._elements.summary.textContent = custom
? SecurityLevelStrings.security_level_custom_summary
: SecurityLevelStrings[`security_level_${level}_summary`];
},
/**
* The popup element.
*
* @type {MozPanel}
*/
get panel() {
if (!this._populated) {
this._populateXUL();
}
return this._elements.panel;
},
init() {
this._securityPrefsBranch = Services.prefs.getBranch(
"browser.security_level."
);
this._securityPrefsBranch.addObserver("", this);
},
uninit() {
this._securityPrefsBranch.removeObserver("", this);
this._securityPrefsBranch = null;
},
hide() {
this._elements.panel.hidePopup();
},
restoreDefaults() {
SecurityLevelPrefs.securityCustom = false;
// Move focus to the settings button since restore defaults button will
// become hidden.
this._elements.settingsButton.focus();
},
openSecuritySettings() {
openPreferences("privacy-securitylevel");
this.hide();
},
// callback when prefs change
observe(subject, topic, data) {
switch (topic) {
case "nsPref:changed":
if (data == "security_slider" || data == "security_custom") {
this._configUIFromPrefs();
}
break;
}
},
}; /* SecurityLevelPanel */
/*
Security Level Preferences Code
Code to handle init and update of security level section in about:preferences#privacy
*/
var SecurityLevelPreferences = {
_securityPrefsBranch: null,
/**
* The notification box shown when the user has a custom security setting.
*
* @type {Element}
*/
_customNotification: null,
/**
* The radiogroup for this preference.
*
* @type {Element}
*/
_radiogroup: null,
/**
* A list of radio options and their containers.
*
* @type {Array<object>}
*/
_radioOptions: null,
_populateXUL() {
this._customNotification = document.getElementById(
"securityLevel-customNotification"
);
this._radiogroup = document.getElementById("securityLevel-radiogroup");
document.querySelector("#securityLevel-groupbox h2").textContent =
SecurityLevelStrings.security_level;
document.getElementById("securityLevel-overview").textContent =
SecurityLevelStrings.security_level_overview;
document
.getElementById("securityLevel-learnMore")
.setAttribute("value", SecurityLevelStrings.security_level_learn_more);
document.getElementById("securityLevel-customHeading").textContent =
SecurityLevelStrings.security_level_custom_heading;
document.getElementById("securityLevel-customDescription").textContent =
SecurityLevelStrings.security_level_custom_summary;
const restoreDefaultsButton = document.getElementById(
"securityLevel-restoreDefaults"
);
restoreDefaultsButton.textContent =
SecurityLevelStrings.security_level_restore;
this._radioOptions = Array.from(
this._radiogroup.querySelectorAll(".securityLevel-radio-option"),
container => {
return { container, radio: container.querySelector("radio") };
}
);
const descListItemsMap = {
safer: [
SecurityLevelStrings.security_level_js_https_only,
SecurityLevelStrings.security_level_limit_typography,
SecurityLevelStrings.security_level_limit_media,
],
safest: [
SecurityLevelStrings.security_level_js_disabled,
SecurityLevelStrings.security_level_limit_typography_svg,
SecurityLevelStrings.security_level_limit_media,
],
};
for (const { container, radio } of this._radioOptions) {
const level = radio.value;
radio.setAttribute(
"label",
SecurityLevelStrings[`security_level_${level}`]
);
container.querySelector(".summary").textContent =
SecurityLevelStrings[`security_level_${level}_summary`];
const descListItems = descListItemsMap[level];
if (!descListItems) {
continue;
}
const descrList = container.querySelector(
".securityLevel-descriptionList"
);
// TODO: Add the elements in securityLevelPreferences.inc.xhtml again
// when we switch to Fluent
for (const text of descListItems) {
let elem = document.createXULElement("description");
elem.textContent = text;
elem.className = "indent";
descrList.append(elem);
}
}
restoreDefaultsButton.addEventListener("command", () => {
SecurityLevelPrefs.securityCustom = false;
});
this._radiogroup.addEventListener("select", () => {
SecurityLevelPrefs.securityLevel = this._radiogroup.value;
});
},
_configUIFromPrefs() {
this._radiogroup.value = SecurityLevelPrefs.securityLevel;
const isCustom = SecurityLevelPrefs.securityCustom;
this._radiogroup.disabled = isCustom;
this._customNotification.hidden = !isCustom;
// Have the container's selection CSS class match the selection state of the
// radio elements.
for (const { container, radio } of this._radioOptions) {
container.classList.toggle(
"securityLevel-radio-option-selected",
radio.selected
);
}
},
init() {
// populate XUL with localized strings
this._populateXUL();
// read prefs and populate UI
this._configUIFromPrefs();
// register for pref chagnes
this._securityPrefsBranch = Services.prefs.getBranch(
"browser.security_level."
);
this._securityPrefsBranch.addObserver("", this);
},
uninit() {
// unregister for pref change events
this._securityPrefsBranch.removeObserver("", this);
this._securityPrefsBranch = null;
},
// callback for when prefs change
observe(subject, topic, data) {
switch (topic) {
case "nsPref:changed":
if (data == "security_slider" || data == "security_custom") {
this._configUIFromPrefs();
}
break;
}
},
}; /* SecurityLevelPreferences */
toolbarbutton#security-level-button[level="standard"] {
list-style-image: url("chrome://browser/content/securitylevel/securityLevelIcon.svg#standard");
}
toolbarbutton#security-level-button[level="safer"] {
list-style-image: url("chrome://browser/content/securitylevel/securityLevelIcon.svg#safer");
}
toolbarbutton#security-level-button[level="safest"] {
list-style-image: url("chrome://browser/content/securitylevel/securityLevelIcon.svg#safest");
}
toolbarbutton#security-level-button[level="standard_custom"] {
list-style-image: url("chrome://browser/content/securitylevel/securityLevelIcon.svg#standard_custom");
}
toolbarbutton#security-level-button[level="safer_custom"] {
list-style-image: url("chrome://browser/content/securitylevel/securityLevelIcon.svg#safer_custom");
}
toolbarbutton#security-level-button[level="safest_custom"] {
list-style-image: url("chrome://browser/content/securitylevel/securityLevelIcon.svg#safest_custom");
}
\ No newline at end of file
<toolbarbutton id="security-level-button"
class="toolbarbutton-1 chromeclass-toolbar-additional"
badged="true"
removable="true"
cui-areatype="toolbar"/>
<svg width="16" height="16" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<style>
use:not(:target) {
display: none;
}
</style>
<defs>
<g id="standard_icon" stroke="none" stroke-width="1">
<path clip-rule="evenodd" d="m8.49614.283505c-.30743-.175675-.68485-.175675-.99228.000001l-6 3.428574c-.31157.17804-.50386.50938-.50386.86824v1.41968c0 4 2.98667 9.0836 7 10 4.0133-.9164 7-6 7-10v-1.41968c0-.35886-.1923-.6902-.5039-.86824zm-.49614 1.216495-5.75 3.28571v1.2746c0 1.71749.65238 3.7522 1.78726 5.46629 1.07287 1.6204 2.47498 2.8062 3.96274 3.2425 1.48776-.4363 2.8899-1.6221 3.9627-3.2425 1.1349-1.71409 1.7873-3.7488 1.7873-5.46629v-1.2746z" fill-rule="evenodd" />
</g>
<g id="safer_icon" stroke="none" stroke-width="1">
<path clip-rule="evenodd" d="m8.49614.283505c-.30743-.175675-.68485-.175675-.99228.000001l-6 3.428574c-.31157.17804-.50386.50938-.50386.86824v1.41968c0 4 2.98667 9.0836 7 10 4.0133-.9164 7-6 7-10v-1.41968c0-.35886-.1923-.6902-.5039-.86824zm-.49614 1.216495-5.75 3.28571v1.2746c0 1.71749.65238 3.7522 1.78726 5.46629 1.07287 1.6204 2.47498 2.8062 3.96274 3.2425 1.48776-.4363 2.8899-1.6221 3.9627-3.2425 1.1349-1.71409 1.7873-3.7488 1.7873-5.46629v-1.2746z" fill-rule="evenodd"/>
<path d="m3.5 6.12062v-.40411c0-.08972.04807-.17255.12597-.21706l4-2.28572c.16666-.09523.37403.02511.37403.21707v10.0766c-1.01204-.408-2.054-1.3018-2.92048-2.6105-1.02134-1.54265-1.57952-3.34117-1.57952-4.77628z"/>
</g>
<g id="safest_icon" stroke="none" stroke-width="1">
<path clip-rule="evenodd" d="m8.49614.283505c-.30743-.175675-.68485-.175675-.99228.000001l-6 3.428574c-.31157.17804-.50386.50938-.50386.86824v1.41968c0 4 2.98667 9.0836 7 10 4.0133-.9164 7-6 7-10v-1.41968c0-.35886-.1923-.6902-.5039-.86824zm-.49614 1.216495-5.75 3.28571v1.2746c0 1.71749.65238 3.7522 1.78726 5.46629 1.07287 1.6204 2.47498 2.8062 3.96274 3.2425 1.48776-.4363 2.8899-1.6221 3.9627-3.2425 1.1349-1.71409 1.7873-3.7488 1.7873-5.46629v-1.2746z" fill-rule="evenodd"/>
<path d="m3.5 6.12062v-.40411c0-.08972.04807-.17255.12597-.21706l4.25-2.42857c.07685-.04392.17121-.04392.24806 0l4.24997 2.42857c.0779.04451.126.12734.126.21706v.40411c0 1.43511-.5582 3.23363-1.5795 4.77628-.8665 1.3087-1.90846 2.2025-2.9205 2.6105-1.01204-.408-2.054-1.3018-2.92048-2.6105-1.02134-1.54265-1.57952-3.34117-1.57952-4.77628z"/>
</g>
<g id="standard_custom_icon" stroke="none" stroke-width="1">
<path d="m9.37255.784312-.87641-.500806c-.30743-.175676-.68485-.175676-.99228 0l-6 3.428574c-.31157.17804-.50386.50938-.50386.86824v1.41968c0 4 2.98667 9.0836 7 10 3.7599-.8585 6.6186-5.3745 6.9647-9.23043-.4008.20936-.8392.35666-1.3024.42914-.2132 1.43414-.8072 2.98009-1.6996 4.32789-1.0728 1.6204-2.47494 2.8062-3.9627 3.2425-1.48776-.4363-2.88987-1.6221-3.96274-3.2425-1.13488-1.71409-1.78726-3.7488-1.78726-5.46629v-1.2746l5.75-3.28571.86913.49664c.10502-.43392.27664-.84184.50342-1.212328z"/>
<circle cx="13" cy="3" fill="#ffbd2e" r="3"/>
</g>
<g id="safer_custom_icon" stroke="none" stroke-width="1">
<path d="m9.37255.784312-.87641-.500806c-.30743-.175676-.68485-.175676-.99228 0l-6 3.428574c-.31157.17804-.50386.50938-.50386.86824v1.41968c0 4 2.98667 9.0836 7 10 3.7599-.8585 6.6186-5.3745 6.9647-9.23043-.4008.20936-.8392.35666-1.3024.42914-.2132 1.43414-.8072 2.98009-1.6996 4.32789-1.0728 1.6204-2.47494 2.8062-3.9627 3.2425-1.48776-.4363-2.88987-1.6221-3.96274-3.2425-1.13488-1.71409-1.78726-3.7488-1.78726-5.46629v-1.2746l5.75-3.28571.86913.49664c.10502-.43392.27664-.84184.50342-1.212328z"/>
<path d="m3.5 6.12062v-.40411c0-.08972.04807-.17255.12597-.21706l4-2.28572c.16666-.09523.37403.02511.37403.21707v10.0766c-1.01204-.408-2.054-1.3018-2.92048-2.6105-1.02134-1.54265-1.57952-3.34117-1.57952-4.77628z"/>
<circle cx="13" cy="3" fill="#ffbd2e" r="3"/>
</g>
<g id="safest_custom_icon" stroke="none" stroke-width="1">
<path d="m9.37255.784312-.87641-.500806c-.30743-.175676-.68485-.175676-.99228 0l-6 3.428574c-.31157.17804-.50386.50938-.50386.86824v1.41968c0 4 2.98667 9.0836 7 10 3.7599-.8585 6.6186-5.3745 6.9647-9.23043-.4008.20936-.8392.35666-1.3024.42914-.2132 1.43414-.8072 2.98009-1.6996 4.32789-1.0728 1.6204-2.47494 2.8062-3.9627 3.2425-1.48776-.4363-2.88987-1.6221-3.96274-3.2425-1.13488-1.71409-1.78726-3.7488-1.78726-5.46629v-1.2746l5.75-3.28571.86913.49664c.10502-.43392.27664-.84184.50342-1.212328z"/>
<path d="m8.77266 3.44151-.64863-.37064c-.07685-.04392-.17121-.04392-.24806 0l-4.25 2.42857c-.0779.04451-.12597.12735-.12597.21706v.40412c0 1.4351.55818 3.23362 1.57952 4.77618.86648 1.3087 1.90844 2.2026 2.92048 2.6106 1.01204-.408 2.054-1.3018 2.9205-2.6106.7761-1.17217 1.2847-2.49215 1.4843-3.68816-1.9219-.26934-3.43158-1.82403-3.63214-3.76713z"/>
<circle cx="13" cy="3" fill="#ffbd2e" r="3"/>
</g>
</defs>
<use id="standard" fill="context-fill" fill-opacity="context-fill-opacity" href="#standard_icon" />
<use id="safer" fill="context-fill" fill-opacity="context-fill-opacity" href="#safer_icon" />
<use id="safest" fill="context-fill" fill-opacity="context-fill-opacity" href="#safest_icon" />
<use id="standard_custom" fill="context-fill" fill-opacity="context-fill-opacity" href="#standard_custom_icon" />
<use id="safer_custom" fill="context-fill" fill-opacity="context-fill-opacity" href="#safer_custom_icon" />
<use id="safest_custom" fill="context-fill" fill-opacity="context-fill-opacity" href="#safest_custom_icon" />
</svg>