[tbb-commits] [tor-browser] 02/02: Bug 40926: Implemented the New Identity feature
gitolite role
git at cupani.torproject.org
Wed Jul 27 14:23:13 UTC 2022
This is an automated email from the git hooks/post-receive script.
richard pushed a commit to branch tor-browser-91.11.0esr-12.0-1
in repository tor-browser.
commit c0426df7f7b372c11bbc16d0f78fcf7db5502acc
Author: Pier Angelo Vendrame <pierov at torproject.org>
AuthorDate: Mon Jul 25 10:40:35 2022 +0200
Bug 40926: Implemented the New Identity feature
---
browser/base/content/appmenu-viewcache.inc.xhtml | 12 +-
browser/base/content/browser-menubar.inc | 5 +-
browser/base/content/browser-sets.inc | 2 +-
browser/base/content/browser.js | 10 +
browser/base/content/navigator-toolbox.inc.xhtml | 5 +-
browser/components/moz.build | 1 +
.../components/newidentity/content/newidentity.js | 566 +++++++++++++++++++++
browser/components/newidentity/jar.mn | 13 +
.../locale/en-US/newIdentity.properties | 8 +
browser/components/newidentity/moz.build | 1 +
browser/installer/package-manifest.in | 2 +
11 files changed, 608 insertions(+), 17 deletions(-)
diff --git a/browser/base/content/appmenu-viewcache.inc.xhtml b/browser/base/content/appmenu-viewcache.inc.xhtml
index a473509f1647c..b3309c3e91fb0 100644
--- a/browser/base/content/appmenu-viewcache.inc.xhtml
+++ b/browser/base/content/appmenu-viewcache.inc.xhtml
@@ -65,11 +65,9 @@
command="Browser:RestoreLastSession"
hidden="true"/>
<toolbarseparator/>
- <toolbarbutton id="appMenuNewIdentity"
+ <toolbarbutton id="appMenu-new-identity"
class="subviewbutton subviewbutton-iconic"
- key="torbutton-new-identity-key"
- label="&torbutton.context_menu.new_identity;"
- oncommand="torbutton_new_identity();"/>
+ key="new-identity-key"/>
<toolbarbutton id="appMenuNewCircuit"
class="subviewbutton subviewbutton-iconic"
key="torbutton-new-circuit-key"
@@ -269,11 +267,9 @@
key="key_privatebrowsing"
command="Tools:PrivateBrowsing"/>
<toolbarseparator/>
- <toolbarbutton id="appMenuNewIdentity"
+ <toolbarbutton id="appMenu-new-identity2"
class="subviewbutton"
- key="torbutton-new-identity-key"
- label="&torbutton.context_menu.new_identity_sentence_case;"
- oncommand="torbutton_new_identity();"/>
+ key="new-identity-key"/>
<toolbarbutton id="appMenuNewCircuit"
class="subviewbutton"
key="torbutton-new-circuit-key"
diff --git a/browser/base/content/browser-menubar.inc b/browser/base/content/browser-menubar.inc
index d3c272f8dc392..45d462671ccc2 100644
--- a/browser/base/content/browser-menubar.inc
+++ b/browser/base/content/browser-menubar.inc
@@ -40,10 +40,7 @@
#endif
<menuseparator/>
<menuitem id="menu_newIdentity"
- accesskey="&torbutton.context_menu.new_identity_key;"
- key="torbutton-new-identity-key"
- label="&torbutton.context_menu.new_identity;"
- oncommand="torbutton_new_identity();"/>
+ key="new-identity-key"/>
<menuitem id="menu_newCircuit"
accesskey="&torbutton.context_menu.new_circuit_key;"
key="torbutton-new-circuit-key"
diff --git a/browser/base/content/browser-sets.inc b/browser/base/content/browser-sets.inc
index c3129d6aae077..51407a2ad4367 100644
--- a/browser/base/content/browser-sets.inc
+++ b/browser/base/content/browser-sets.inc
@@ -383,6 +383,6 @@
data-l10n-id="hide-other-apps-shortcut"
modifiers="accel,alt"/>
#endif
- <key id="torbutton-new-identity-key" modifiers="accel shift" key="U" oncommand="torbutton_new_identity()"/>
+ <key id="new-identity-key" modifiers="accel shift" key="U" oncommand="NewIdentityButton.onCommand(event)"/>
<key id="torbutton-new-circuit-key" modifiers="accel shift" key="L" oncommand="torbutton_new_circuit()"/>
</keyset>
diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js
index faf6433ccacf8..4dff70094d667 100644
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -230,6 +230,11 @@ XPCOMUtils.defineLazyScriptGetter(
["SecurityLevelButton"],
"chrome://browser/content/securitylevel/securityLevel.js"
);
+XPCOMUtils.defineLazyScriptGetter(
+ this,
+ ["NewIdentityButton"],
+ "chrome://browser/content/newidentity.js"
+);
XPCOMUtils.defineLazyScriptGetter(
this,
["OnionAuthPrompt"],
@@ -1778,6 +1783,9 @@ var gBrowserInit = {
// Init the SecuritySettingsButton
SecurityLevelButton.init();
+ // Init the NewIdentityButton
+ NewIdentityButton.init();
+
// Init the OnionAuthPrompt
OnionAuthPrompt.init();
@@ -2516,6 +2524,8 @@ var gBrowserInit = {
SecurityLevelButton.uninit();
+ NewIdentityButton.uninit();
+
OnionAuthPrompt.uninit();
TorBootstrapUrlbar.uninit();
diff --git a/browser/base/content/navigator-toolbox.inc.xhtml b/browser/base/content/navigator-toolbox.inc.xhtml
index b881492864ae9..eaaa6c1538d1f 100644
--- a/browser/base/content/navigator-toolbox.inc.xhtml
+++ b/browser/base/content/navigator-toolbox.inc.xhtml
@@ -577,10 +577,7 @@
ondragenter="newWindowButtonObserver.onDragOver(event)"
ondragexit="newWindowButtonObserver.onDragExit(event)"/>
- <toolbarbutton id="new-identity-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
- label="&torbutton.context_menu.new_identity;"
- oncommand="torbutton_new_identity();"
- tooltiptext="&torbutton.context_menu.new_identity;"/>
+ <toolbarbutton id="new-identity-button" class="toolbarbutton-1 chromeclass-toolbar-additional"/>
<toolbarbutton id="new-circuit-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
label="&torbutton.context_menu.new_circuit;"
diff --git a/browser/components/moz.build b/browser/components/moz.build
index 0eefdd0a06349..eedc66029ce69 100644
--- a/browser/components/moz.build
+++ b/browser/components/moz.build
@@ -37,6 +37,7 @@ DIRS += [
"enterprisepolicies",
"extensions",
"migration",
+ "newidentity",
"newtab",
"onionservices",
"originattributes",
diff --git a/browser/components/newidentity/content/newidentity.js b/browser/components/newidentity/content/newidentity.js
new file mode 100644
index 0000000000000..a1d10a910cabe
--- /dev/null
+++ b/browser/components/newidentity/content/newidentity.js
@@ -0,0 +1,566 @@
+"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: `${brandShortName} will close all windows and tabs. All website sessions will be lost. \nRestart ${brandShortName} now to reset your identity?`,
+ new_identity_ask_again: "Never ask me again",
+ new_identity_menu_accesskey: "I",
+ };
+ let bundle = null;
+ try {
+ bundle = Services.strings.createBundle(
+ "chrome://newidentity/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
+ );
+ }
+ 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 = (() => {
+ let scope = {};
+ ChromeUtils.import("resource://gre/modules/Console.jsm", scope);
+ const consoleOptions = {
+ maxLogLevel: "info",
+ prefix: "NewIdentity",
+ };
+ return new scope.ConsoleAPI(consoleOptions);
+ })();
+
+ const topics = Object.freeze({
+ newIdentityRequested: "new-identity-requested",
+ });
+
+ class NewIdentityImpl {
+ async run() {
+ logger.debug("Disabling JS");
+ this.disableAllJS();
+ await this.clearState();
+ this.broadcast();
+ this.openNewWindow();
+ this.closeOldWindow();
+ }
+
+ // 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) {
+ for (const browser of win.gBrowser?.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();
+ await this.clearStorage();
+ this.clearPreferencesAndPermissions();
+ 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("extensions.torbutton.close_newnym")) {
+ 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("extensions.torbutton.clear_http_auth")) {
+ 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();
+ }
+
+ async clearData(flags) {
+ return new Promise((resolve, reject) => {
+ Services.clearData.deleteData(flags, {
+ onDataDeleted(code) {
+ if (code === Cr.NS_OK) {
+ resolve();
+ } else {
+ reject(
+ new Error(`Error deleting data with flags ${flags}: ${code}`)
+ );
+ }
+ },
+ });
+ });
+ }
+
+ 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);
+ }
+ }
+
+ async clearStorage() {
+ logger.info("Clearing Disk and Memory Caches");
+ try {
+ Services.cache2.clear();
+ } catch (e) {
+ logger.error("Exception on cache clearing", e);
+ }
+
+ logger.info("Clearing storage, media devices and predictor network data");
+ try {
+ await this.clearData(
+ Services.clearData.CLEAR_DOM_STORAGES |
+ Services.clearData.CLEAR_MEDIA_DEVICES |
+ Services.clearData.CLEAR_PREDICTOR_NETWORK_DATA
+ );
+ } catch (e) {
+ logger.error("Exception on storage 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);
+ }
+
+ 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");
+ // 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?
+ OpenBrowserWindow();
+ }
+
+ 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() {
+ CustomizableUI.addListener(this);
+
+ const button = document.querySelector("#new-identity-button");
+ if (button) {
+ button.setAttribute("tooltiptext", 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);
+ appButton.addEventListener("command", () => {
+ this.onCommand();
+ });
+ }
+ const appButton2 = viewCache.querySelector("#appMenu-new-identity2");
+ if (appButton2) {
+ appButton2.setAttribute(
+ "label",
+ NewIdentityStrings.new_identity_sentence_case
+ );
+ appButton2.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() {
+ CustomizableUI.removeListener(this);
+ },
+
+ onCustomizeStart(window) {
+ const button = document.querySelector("#new-identity-button");
+ button.setAttribute("label", NewIdentityStrings.new_identity);
+ },
+
+ onWidgetAfterDOMChange(aNode, aNextNode, aContainer, aWasRemoval) {},
+
+ 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 = "extensions.torbutton.confirm_newnym";
+ const shouldConfirm = Services.prefs.getBoolPref(prefConfirm);
+ if (shouldConfirm) {
+ // Display two buttons, both with string titles.
+ const flags = Services.prompt.STD_YES_NO_BUTTONS;
+ const askAgain = { value: false };
+ const confirmed =
+ Services.prompt.confirmEx(
+ null,
+ "",
+ NewIdentityStrings.new_identity_prompt,
+ flags,
+ null,
+ null,
+ null,
+ NewIdentityStrings.new_identity_ask_again,
+ askAgain
+ ) == 0;
+ Services.prefs.setBoolPref(prefConfirm, !askAgain.value);
+ if (!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;
+ }
+ },
+ };
+});
diff --git a/browser/components/newidentity/jar.mn b/browser/components/newidentity/jar.mn
new file mode 100644
index 0000000000000..57b30b32c0e18
--- /dev/null
+++ b/browser/components/newidentity/jar.mn
@@ -0,0 +1,13 @@
+browser.jar:
+ content/browser/newidentity.js (content/newidentity.js)
+
+newidentity.jar:
+# We need to list at least one locale here, to make Firefox load the localized
+# copy of properties at chrome://newidentity/locale/newIdentity.properties.
+# Ideally, we should use @AB_CD at .jar to automatically copy all the locales
+# Firefox is built with. But we only provide English here, and injecting the
+# translated files directly to the omni.ja works better for us, for the time
+# being. In addition to inject the properties files, we also add the
+# corresponding locale line to chrome/chrome.manifest.
+% locale newidentity en-US %locale/en-US/
+ locale/en-US/newIdentity.properties (locale/en-US/newIdentity.properties)
diff --git a/browser/components/newidentity/locale/en-US/newIdentity.properties b/browser/components/newidentity/locale/en-US/newIdentity.properties
new file mode 100644
index 0000000000000..54eeca135a5e1
--- /dev/null
+++ b/browser/components/newidentity/locale/en-US/newIdentity.properties
@@ -0,0 +1,8 @@
+new_identity = New Identity
+# This is the string for the hamburger menu
+new_identity_sentence_case = New identity
+# %S is the application name. Keep it as a placeholder
+new_identity_prompt = %S will close all windows and tabs. All website sessions will be lost. \nRestart %S now to reset your identity?
+new_identity_ask_again = Never ask me again
+# Shown in the File menu (use Alt to show File, if you do not see)
+new_identity_menu_accesskey = I
diff --git a/browser/components/newidentity/moz.build b/browser/components/newidentity/moz.build
new file mode 100644
index 0000000000000..2661ad7cb9f3d
--- /dev/null
+++ b/browser/components/newidentity/moz.build
@@ -0,0 +1 @@
+JAR_MANIFESTS += ["jar.mn"]
diff --git a/browser/installer/package-manifest.in b/browser/installer/package-manifest.in
index 7b1ea43f8ef0e..29aa2639e5d9e 100644
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -262,6 +262,8 @@
@RESPATH@/browser/features/*
; Base Browser
+ at RESPATH@/browser/chrome/newidentity.manifest
+ at RESPATH@/browser/chrome/newidentity/
@RESPATH@/browser/chrome/securitylevel.manifest
@RESPATH@/browser/chrome/securitylevel/
@RESPATH@/browser/components/SecurityLevel.manifest
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the tbb-commits
mailing list