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@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/n... + 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#.... + // 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@.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 +@RESPATH@/browser/chrome/newidentity.manifest +@RESPATH@/browser/chrome/newidentity/ @RESPATH@/browser/chrome/securitylevel.manifest @RESPATH@/browser/chrome/securitylevel/ @RESPATH@/browser/components/SecurityLevel.manifest