1 // Bug 1506 P1-P5: This is the main Torbutton overlay file. Much needs to be
2 // preserved here, but in an ideal world, most of this code should perhaps be
3 // moved into an XPCOM service, and much can also be tossed. See also
4 // individual 1506 comments for details.
6 // TODO: check for leaks: http://www.mozilla.org/scriptable/avoiding-leaks.html
7 // TODO: Double-check there are no strange exploits to defeat:
8 // http://kb.mozillazine.org/Links_to_local_pages_don%27t_work
10 let { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
11 let { showDialog, show_torbrowser_manual } = Cu.import("resource://torbutton/modules/utils.js", {});
12 let { unescapeTorString } = Cu.import("resource://torbutton/modules/utils.js", {});
13 let SecurityPrefs = Cu.import("resource://torbutton/modules/security-prefs.js", {});
14 let { bindPrefAndInit, observe } = Cu.import("resource://torbutton/modules/utils.js", {});
16 const k_tb_last_browser_version_pref = "extensions.torbutton.lastBrowserVersion";
17 const k_tb_browser_update_needed_pref = "extensions.torbutton.updateNeeded";
18 const k_tb_last_update_check_pref = "extensions.torbutton.lastUpdateCheck";
19 const k_tb_tor_check_failed_topic = "Torbutton:TorCheckFailed";
21 var m_tb_prefs = Services.prefs;
24 var m_tb_wasinited = false;
25 var m_tb_plugin_string = false;
26 var m_tb_is_main_window = false;
27 var m_tb_hidden_browser = false;
29 var m_tb_confirming_plugins = false;
31 var m_tb_window_height = window.outerHeight;
32 var m_tb_window_width = window.outerWidth;
36 var m_tb_control_ipc_file = null; // Set if using IPC (UNIX domain socket).
37 var m_tb_control_port = null; // Set if using TCP.
38 var m_tb_control_host = null; // Set if using TCP.
39 var m_tb_control_pass = null;
40 var m_tb_control_desc = null; // For logging.
42 var m_tb_domWindowUtils = window.QueryInterface(Ci.nsIInterfaceRequestor).
43 getInterface(Ci.nsIDOMWindowUtils);
45 // Bug 1506 P1: This object is only for updating the UI for toggling and style
46 var torbutton_window_pref_observer =
50 m_tb_prefs.addObserver("extensions.torbutton", this, false);
53 unregister: function()
55 m_tb_prefs.removeObserver("extensions.torbutton", this);
58 // topic: what event occurred
59 // subject: what nsIPrefBranch we're observing
60 // data: which pref has been changed (relative to subject)
61 observe: function(subject, topic, data)
63 if (topic != "nsPref:changed") return;
65 case k_tb_browser_update_needed_pref:
66 torbutton_notify_if_update_needed();
72 // Bug 1506 P2: This object keeps Firefox prefs in sync with Torbutton prefs.
73 // It probably could stand some simplification (See #3100). It also belongs
74 // in a component, not the XUL overlay.
75 var torbutton_unique_pref_observer =
79 this.forced_ua = false;
80 m_tb_prefs.addObserver("extensions.torbutton", this, false);
81 m_tb_prefs.addObserver("network.cookie", this, false);
82 m_tb_prefs.addObserver("browser.privatebrowsing.autostart", this, false);
83 m_tb_prefs.addObserver("javascript", this, false);
84 m_tb_prefs.addObserver("plugin.disable", this, false);
85 m_tb_prefs.addObserver("privacy.firstparty.isolate", this, false);
86 m_tb_prefs.addObserver("privacy.resistFingerprinting", this, false);
88 // We observe xpcom-category-entry-added for plugins w/ Gecko-Content-Viewers
89 var observerService = Cc["@mozilla.org/observer-service;1"].
90 getService(Ci.nsIObserverService);
91 observerService.addObserver(this, "xpcom-category-entry-added", false);
94 unregister: function()
96 m_tb_prefs.removeObserver("extensions.torbutton", this);
97 m_tb_prefs.removeObserver("network.cookie", this);
98 m_tb_prefs.removeObserver("browser.privatebrowsing.autostart", this);
99 m_tb_prefs.removeObserver("javascript", this);
100 m_tb_prefs.removeObserver("plugin.disable", this);
101 m_tb_prefs.removeObserver("privacy.firstparty.isolate", this);
102 m_tb_prefs.removeObserver("privacy.resistFingerprinting", this);
104 var observerService = Cc["@mozilla.org/observer-service;1"].
105 getService(Ci.nsIObserverService);
106 observerService.removeObserver(this, "xpcom-category-entry-added");
109 // topic: what event occurred
110 // subject: what nsIPrefBranch we're observing
111 // data: which pref has been changed (relative to subject)
112 observe: function(subject, topic, data)
114 if (topic == "xpcom-category-entry-added") {
115 // Hrmm. should we inspect subject too? it's just mime type..
116 subject.QueryInterface(Ci.nsISupportsCString);
117 if (data == "Gecko-Content-Viewers" &&
118 !m_tb_prefs.getBoolPref("extensions.torbutton.startup") &&
119 m_tb_prefs.getBoolPref("extensions.torbutton.confirm_plugins")) {
120 torbutton_log(3, "Got plugin enabled notification: "+subject);
122 /* We need to protect this call with a flag becuase we can
123 * get multiple observer events for each mime type a plugin
124 * registers. Thankfully, these notifications arrive only on
125 * the main thread, *however*, our confirmation dialog suspends
126 * execution and allows more events to arrive until it is answered
128 if (!m_tb_confirming_plugins) {
129 m_tb_confirming_plugins = true;
130 torbutton_confirm_plugins();
131 m_tb_confirming_plugins = false;
133 torbutton_log(3, "Skipping notification for mime type: "+subject);
139 if (topic != "nsPref:changed") return;
142 case "network.cookie.cookieBehavior":
143 let val = m_tb_prefs.getIntPref("network.cookie.cookieBehavior");
144 let firstparty_isolate = m_tb_prefs.getBoolPref("privacy.firstparty.isolate");
145 if (val == 0 && firstparty_isolate) // Allow all cookies
146 m_tb_prefs.setBoolPref("privacy.firstparty.isolate", false);
147 else if (val == 1 && !firstparty_isolate) // Block third party cookies
148 m_tb_prefs.setBoolPref("privacy.firstparty.isolate", true);
151 case "plugin.disable":
152 torbutton_toggle_plugins(
153 m_tb_prefs.getBoolPref("plugin.disable"));
155 case "browser.privatebrowsing.autostart":
156 torbutton_update_disk_prefs();
158 case "extensions.torbutton.use_nontor_proxy":
159 torbutton_use_nontor_proxy();
161 case "privacy.resistFingerprinting":
162 torbutton_update_fingerprinting_prefs();
164 case "privacy.firstparty.isolate":
165 torbutton_update_isolation_prefs();
171 var torbutton_tor_check_observer = {
174 this._obsSvc = Cc["@mozilla.org/observer-service;1"]
175 .getService(Ci.nsIObserverService);
176 this._obsSvc.addObserver(this, k_tb_tor_check_failed_topic, false);
179 unregister: function()
182 this._obsSvc.removeObserver(this, k_tb_tor_check_failed_topic);
185 observe: function(subject, topic, data)
187 if (topic == k_tb_tor_check_failed_topic) {
188 // Update toolbar icon and tooltip.
189 torbutton_update_toolbutton();
191 // Update all open about:tor pages.
192 torbutton_abouttor_message_handler.updateAllOpenPages();
194 // If the user does not have an about:tor tab open in the front most
196 var wm = Cc["@mozilla.org/appshell/window-mediator;1"]
197 .getService(Components.interfaces.nsIWindowMediator);
198 var win = wm.getMostRecentWindow("navigator:browser");
200 let foundTab = false;
201 let tabBrowser = top.getBrowser();
202 for (let i = 0; !foundTab && (i < tabBrowser.browsers.length); ++i) {
203 let b = tabBrowser.getBrowserAtIndex(i);
204 foundTab = (b.currentURI.spec.toLowerCase() == "about:tor");
208 gBrowser.selectedTab = gBrowser.addTab("about:tor");
214 function torbutton_init_toolbutton()
217 torbutton_log(3, "Initializing the Torbutton button.");
218 torbutton_update_toolbutton();
220 torbutton_log(4, "Error Initializing Torbutton button: "+e);
224 function torbutton_is_mobile() {
225 return Services.appinfo.OS === "Android";
228 // Bug 1506 P2-P4: This code sets some version variables that are irrelevant.
229 // It does read out some important environment variables, though. It is
230 // called once per browser window.. This might belong in a component.
231 function torbutton_init() {
232 torbutton_log(3, 'called init()');
234 SecurityPrefs.initialize();
236 if (m_tb_wasinited) {
239 m_tb_wasinited = true;
241 // Determine if we are running inside Tor Browser.
244 cur_version = m_tb_prefs.getCharPref("torbrowser.version");
246 torbutton_log(3, "This is a Tor Browser");
248 torbutton_log(3, "This is not a Tor Browser: "+e);
251 // If the Tor Browser version has changed since the last time Torbutton
252 // was loaded, reset the version check preferences in order to avoid
253 // incorrectly reporting that the browser needs to be updated.
256 last_version = m_tb_prefs.getCharPref(k_tb_last_browser_version_pref);
258 if (cur_version != last_version) {
259 m_tb_prefs.setBoolPref(k_tb_browser_update_needed_pref, false);
260 if (m_tb_prefs.prefHasUserValue(k_tb_last_update_check_pref)) {
261 m_tb_prefs.clearUserPref(k_tb_last_update_check_pref);
265 m_tb_prefs.setCharPref(k_tb_last_browser_version_pref, cur_version);
270 tlps = Cc["@torproject.org/torlauncher-protocol-service;1"]
271 .getService(Ci.nsISupports).wrappedJSObject;
274 // Bug 1506 P4: These vars are very important for New Identity
275 var environ = Components.classes["@mozilla.org/process/environment;1"]
276 .getService(Components.interfaces.nsIEnvironment);
278 if (environ.exists("TOR_CONTROL_PASSWD")) {
279 m_tb_control_pass = environ.get("TOR_CONTROL_PASSWD");
280 } else if (environ.exists("TOR_CONTROL_COOKIE_AUTH_FILE")) {
281 var cookie_path = environ.get("TOR_CONTROL_COOKIE_AUTH_FILE");
283 if ("" != cookie_path) {
284 m_tb_control_pass = torbutton_read_authentication_cookie(cookie_path);
287 torbutton_log(4, 'unable to read authentication cookie');
290 // Try to get password from Tor Launcher.
291 m_tb_control_pass = tlps.TorGetPassword(false);
294 // Try to get the control port IPC file (an nsIFile) from Tor Launcher,
295 // since Tor Launcher knows how to handle its own preferences and how to
296 // resolve relative paths.
298 m_tb_control_ipc_file = tlps.TorGetControlIPCFile();
301 if (m_tb_control_ipc_file) {
302 m_tb_control_desc = m_tb_control_ipc_file.path;
304 if (environ.exists("TOR_CONTROL_PORT")) {
305 m_tb_control_port = environ.get("TOR_CONTROL_PORT");
308 const kTLControlPortPref = "extensions.torlauncher.control_port";
309 m_tb_control_port = m_tb_prefs.getIntPref(kTLControlPortPref);
311 // Since we want to disable some features when Tor Launcher is
312 // not installed (e.g., New Identity), we do not set a default
317 if (m_tb_control_port) {
318 m_tb_control_desc = "" + m_tb_control_port;
321 if (environ.exists("TOR_CONTROL_HOST")) {
322 m_tb_control_host = environ.get("TOR_CONTROL_HOST");
325 const kTLControlHostPref = "extensions.torlauncher.control_host";
326 m_tb_control_host = m_tb_prefs.getCharPref(kTLControlHostPref);
328 m_tb_control_host = "127.0.0.1";
333 // Add about:tor IPC message listener.
334 window.messageManager.addMessageListener("AboutTor:Loaded",
335 torbutton_abouttor_message_handler);
337 setupPreferencesForMobile();
339 // XXX: Get rid of the cached asmjs (or IndexedDB) files on disk in case we
340 // don't allow things saved to disk. This is an ad-hoc fix to work around
341 // #19417. Once this is properly solved we should remove this code again.
342 if (m_tb_prefs.getBoolPref("browser.privatebrowsing.autostart")) {
343 let orig_quota_test = m_tb_prefs.getBoolPref("dom.quotaManager.testing");
345 // This works only by setting the pref to `true` otherwise we get an
346 // exception and nothing is happening.
347 m_tb_prefs.setBoolPref("dom.quotaManager.testing", true);
348 Cc["@mozilla.org/dom/quota-manager-service;1"]
349 .getService(Ci.nsIQuotaManagerService).clear();
352 m_tb_prefs.setBoolPref("dom.quotaManager.testing", orig_quota_test);
356 // listen for our toolbar button being added so we can initialize it
357 torbutton_init_toolbutton();
359 torbutton_log(1, 'registering pref observer');
360 torbutton_window_pref_observer.register();
362 torbutton_log(1, "registering Tor check observer");
363 torbutton_tor_check_observer.register();
365 //setting up context menu
366 //var contextMenu = document.getElementById("contentAreaContextMenu");
368 // contextMenu.addEventListener("popupshowing", torbutton_check_contextmenu, false);
370 // Add toolbutton to the bar.
371 // This should maybe be in the startup function, but we want to add
372 // the button to the panel before it's state (color) is set..
373 if (!m_tb_prefs.getBoolPref("extensions.torbutton.inserted_button")) {
374 torbutton_log(3, 'Adding button');
376 // ESR31-style toolbar is handled by the existing compiled-in pref.
377 // We also need to prevent first-run toolbar reorg (#13378), so we
378 // reset this toolbar state on first-run.
380 m_tb_prefs.clearUserPref("browser.uiCustomization.state");
382 CustomizableUI.reset();
383 CustomizableUI.undoReset();
384 torbutton_log(3, 'Button added');
385 m_tb_prefs.setBoolPref("extensions.torbutton.inserted_button", true);
387 torbutton_log(4, 'Failed to add Torbutton to toolbar: '+e);
391 torbutton_update_toolbutton();
392 torbutton_notify_if_update_needed();
395 createTorCircuitDisplay(m_tb_control_ipc_file, m_tb_control_host,
396 m_tb_control_port, m_tb_control_pass,
397 "extensions.torbutton.display_circuit");
399 torbutton_log(4, "Error creating the tor circuit display " + e);
403 torbutton_init_user_manual_links();
405 torbutton_log(4, "Error loading the user manual " + e);
408 // Arrange for our about:tor content script to be loaded in each frame.
409 window.messageManager.loadFrameScript(
410 "chrome://torbutton/content/aboutTor/aboutTor-content.js", true);
412 torbutton_log(3, 'init completed');
415 var torbutton_abouttor_message_handler = {
416 // Receive IPC messages from the about:tor content script.
417 receiveMessage: function(aMessage) {
418 switch(aMessage.name) {
419 case "AboutTor:Loaded":
420 aMessage.target.messageManager.sendAsyncMessage("AboutTor:ChromeData",
426 // Send privileged data to all of the about:tor content scripts.
427 updateAllOpenPages: function() {
428 window.messageManager.broadcastAsyncMessage("AboutTor:ChromeData",
432 // The chrome data contains all of the data needed by the about:tor
433 // content process that is only available here (in the chrome process).
434 // It is sent to the content process when an about:tor window is opened
435 // and in response to events such as the browser noticing that Tor is
439 mobile: torbutton_is_mobile(),
440 torOn: torbutton_tor_check_ok()
445 function torbutton_confirm_plugins() {
446 var any_plugins_enabled = false;
447 var PH=Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
448 var P=PH.getPluginTags({});
449 for(var i=0; i<P.length; i++) {
451 any_plugins_enabled = true;
454 if (!any_plugins_enabled) {
455 torbutton_log(3, "False positive on plugin notification. Ignoring");
459 torbutton_log(3, "Confirming plugin usage.");
461 var prompts = Cc["@mozilla.org/embedcomp/prompt-service;1"]
462 .getService(Components.interfaces.nsIPromptService);
464 // Display two buttons, both with string titles.
465 var flags = prompts.STD_YES_NO_BUTTONS + prompts.BUTTON_DELAY_ENABLE;
467 var message = torbutton_get_property_string("torbutton.popup.confirm_plugins");
468 var askAgainText = torbutton_get_property_string("torbutton.popup.never_ask_again");
469 var askAgain = {value: false};
471 var wm = Cc["@mozilla.org/appshell/window-mediator;1"]
472 .getService(Components.interfaces.nsIWindowMediator);
473 var win = wm.getMostRecentWindow("navigator:browser");
474 var no_plugins = (prompts.confirmEx(win, "", message, flags, null, null, null,
475 askAgainText, askAgain) == 1);
477 m_tb_prefs.setBoolPref("extensions.torbutton.confirm_plugins", !askAgain.value);
479 // The pref observer for "plugin.disable" will set the appropriate plugin state.
480 // So, we only touch the pref if it has changed.
482 m_tb_prefs.getBoolPref("plugin.disable"))
483 m_tb_prefs.setBoolPref("plugin.disable", no_plugins);
485 torbutton_toggle_plugins(no_plugins);
487 // Now, if any tabs were open to about:addons, reload them. Our popup
488 // messed up that page.
489 var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
490 .getService(Components.interfaces.nsIWindowMediator);
491 var browserEnumerator = wm.getEnumerator("navigator:browser");
493 // Check each browser instance for our URL
494 while (browserEnumerator.hasMoreElements()) {
495 var browserWin = browserEnumerator.getNext();
496 var tabbrowser = browserWin.gBrowser;
498 // Check each tab of this browser instance
499 var numTabs = tabbrowser.browsers.length;
500 for (var index = 0; index < numTabs; index++) {
501 var currentBrowser = tabbrowser.getBrowserAtIndex(index);
502 if ("about:addons" == currentBrowser.currentURI.spec) {
503 torbutton_log(3, "Got browser: "+currentBrowser.currentURI.spec);
504 currentBrowser.reload();
510 function torbutton_inform_about_tbb() {
511 var prompts = Cc["@mozilla.org/embedcomp/prompt-service;1"]
512 .getService(Components.interfaces.nsIPromptService);
514 var message = torbutton_get_property_string("torbutton.popup.prompt_torbrowser");
515 var title = torbutton_get_property_string("torbutton.title.prompt_torbrowser");
516 var checkbox = {value: false};
518 var sb = Components.classes["@mozilla.org/intl/stringbundle;1"]
519 .getService(Components.interfaces.nsIStringBundleService);
520 var browserstrings = sb.createBundle("chrome://browser/locale/browser.properties");
522 var askagain = browserstrings.GetStringFromName("privateBrowsingNeverAsk");
524 var response = prompts.alertCheck(null, title, message, askagain, checkbox);
526 // Update preferences to reflect their response and to prevent the prompt from
527 // being displayed again.
528 m_tb_prefs.setBoolPref("extensions.torbutton.prompt_torbrowser", !checkbox.value);
531 // Bug 1506 P2: It might be nice to let people move the button around, I guess?
532 function torbutton_get_toolbutton() {
533 var o_toolbutton = false;
535 torbutton_log(1, 'get_toolbutton(): looking for button element');
536 if (document.getElementById("torbutton-button")) {
537 o_toolbutton = document.getElementById("torbutton-button");
538 } else if (document.getElementById("torbutton-button-tb")) {
539 o_toolbutton = document.getElementById("torbutton-button-tb");
540 } else if (document.getElementById("torbutton-button-tb-msg")) {
541 o_toolbutton = document.getElementById("torbutton-button-tb-msg");
543 torbutton_log(3, 'get_toolbutton(): did not find torbutton-button');
549 function torbutton_update_is_needed() {
550 var updateNeeded = false;
552 updateNeeded = m_tb_prefs.getBoolPref(k_tb_browser_update_needed_pref);
558 function torbutton_notify_if_update_needed() {
559 function setOrClearAttribute(aElement, aAttrName, aValue)
561 if (!aElement || !aAttrName)
565 aElement.setAttribute(aAttrName, aValue);
567 aElement.removeAttribute(aAttrName);
570 let updateNeeded = torbutton_update_is_needed();
572 // Change look of toolbar item (enable/disable animated update icon).
573 var btn = torbutton_get_toolbutton();
574 setOrClearAttribute(btn, "tbUpdateNeeded", updateNeeded);
576 // Make the "check for update" menu item bold if an update is needed.
577 var item = document.getElementById("torbutton-checkForUpdate");
578 setOrClearAttribute(item, "tbUpdateNeeded", updateNeeded);
581 function torbutton_check_for_update() {
582 // Open the update prompt in the correct mode. The update state
583 // checks used here were adapted from isPending() and isApplied() in
584 // Mozilla's browser/base/content/aboutDialog.js code.
585 let updateMgr = Cc["@mozilla.org/updates/update-manager;1"]
586 .getService(Ci.nsIUpdateManager);
587 let update = updateMgr.activeUpdate;
588 let updateState = (update) ? update.state : undefined;
589 let pendingStates = [ "pending", "pending-service",
590 "applied", "applied-service" ];
591 let isPending = (updateState && (pendingStates.indexOf(updateState) >= 0));
593 let prompter = Cc["@mozilla.org/updates/update-prompt;1"]
594 .createInstance(Ci.nsIUpdatePrompt);
596 prompter.showUpdateDownloaded(update, false);
598 prompter.checkForUpdates();
601 // Bug 1506 P4: Checking for Tor Browser updates is pretty important,
602 // probably even as a fallback if we ever do get a working updater.
603 function torbutton_do_async_versioncheck() {
604 if (!m_tb_tbb || !m_tb_prefs.getBoolPref("extensions.torbutton.versioncheck_enabled")) {
608 // Suppress update check if done recently.
609 const kMinSecsBetweenChecks = 120 * 60; // 2.0 hours
610 var now = Date.now() / 1000;
613 lastCheckTime = parseFloat(m_tb_prefs.getCharPref(k_tb_last_update_check_pref));
614 if (isNaN(lastCheckTime))
615 lastCheckTime = undefined;
618 if (lastCheckTime && ((now - lastCheckTime) < kMinSecsBetweenChecks))
621 m_tb_prefs.setCharPref(k_tb_last_update_check_pref, now);
623 torbutton_log(3, "Checking version with socks port: "
624 +m_tb_prefs.getIntPref("network.proxy.socks_port"));
626 var req = new XMLHttpRequest();
627 var url = m_tb_prefs.getCharPref("extensions.torbutton.versioncheck_url");
628 req.open('GET', url, true);
629 req.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE;
630 req.overrideMimeType("text/json");
631 req.onreadystatechange = function (oEvent) {
632 if (req.readyState === 4) {
633 if(req.status == 200) {
634 if(!req.responseText) {
635 torbutton_log(5, "Version check failed! No JSON present!");
639 var version_list = JSON.parse(req.responseText);
640 var my_version = m_tb_prefs.getCharPref("torbrowser.version");
642 var platform = Services.appinfo.OS;
645 platformSuffix = "Windows";
648 platformSuffix = "MacOS";
652 platformSuffix = platform;
656 my_version += "-" + platformSuffix;
658 if (version_list.indexOf(my_version) >= 0) {
659 torbutton_log(3, "Version check passed.");
660 m_tb_prefs.setBoolPref(k_tb_browser_update_needed_pref, false);
663 torbutton_log(5, "Your Tor Browser is out of date.");
664 m_tb_prefs.setBoolPref(k_tb_browser_update_needed_pref, true);
667 torbutton_log(5, "Version check failed! JSON parsing error: "+e);
670 } else if (req.status == 404) {
671 // We're going to assume 404 means the service is not implemented yet.
672 torbutton_log(3, "Version check failed. Versions file is 404.");
675 torbutton_log(5, "Version check failed! Web server error: "+req.status);
681 if(e.result == 0x80004005) { // NS_ERROR_FAILURE
682 torbutton_log(5, "Version check failed! Is tor running?");
685 torbutton_log(5, "Version check failed! Tor internal error: "+e);
691 function torbutton_update_toolbutton()
693 let o_toolbutton = torbutton_get_toolbutton();
694 if (!o_toolbutton) return;
696 let isOK = torbutton_tor_check_ok();
697 let tbstatus = isOK ? "on" : "off";
698 o_toolbutton.setAttribute("tbstatus", tbstatus);
700 let tooltipKey = isOK ? "torbutton.panel.label.enabled"
701 : "torbutton.panel.label.disabled";
702 o_toolbutton.setAttribute("tooltiptext",
703 torbutton_get_property_string(tooltipKey));
706 // Bug 1506 P4: Control port interaction. Needed for New Identity.
707 function torbutton_socket_readline(input) {
710 while((bytes = input.readBytes(1)) != "\n") {
717 // Bug 1506 P4: Control port interaction. Needed for New Identity.
718 function torbutton_read_authentication_cookie(path) {
719 var file = Components.classes['@mozilla.org/file/local;1']
720 .createInstance(Components.interfaces.nsIFile);
721 file.initWithPath(path);
722 var fileStream = Components.classes['@mozilla.org/network/file-input-stream;1']
723 .createInstance(Components.interfaces.nsIFileInputStream);
724 fileStream.init(file, 1, 0, false);
725 var binaryStream = Components.classes['@mozilla.org/binaryinputstream;1']
726 .createInstance(Components.interfaces.nsIBinaryInputStream);
727 binaryStream.setInputStream(fileStream);
728 var array = binaryStream.readByteArray(fileStream.available());
729 binaryStream.close();
731 return torbutton_array_to_hexdigits(array);
734 // Bug 1506 P4: Control port interaction. Needed for New Identity.
735 function torbutton_array_to_hexdigits(array) {
736 return array.map(function(c) {
737 return String("0" + c.toString(16)).slice(-2)
741 // Bug 1506 P4: Control port interaction. Needed for New Identity.
743 // Executes a command on the control port.
744 // Return a string response upon success and null upon error.
745 function torbutton_send_ctrl_cmd(command) {
747 // We spin the event queue until it is empty and we can be sure that sending
748 // NEWNYM is not leading to a deadlock (see bug 9531 comment 23 for an
749 // invstigation on why and when this may happen). This is surrounded by
750 // suppressing/unsuppressing user initiated events in a window's document to
751 // be sure that these events are not interfering with processing events being
752 // in the event queue.
753 var thread = Cc["@mozilla.org/thread-manager;1"].
754 getService(Ci.nsIThreadManager).currentThread;
755 m_tb_domWindowUtils.suppressEventHandling(true);
756 while (thread.processNextEvent(false)) {}
757 m_tb_domWindowUtils.suppressEventHandling(false);
760 let sts = Cc["@mozilla.org/network/socket-transport-service;1"]
761 .getService(Ci.nsISocketTransportService);
763 if (m_tb_control_ipc_file) {
764 socket = sts.createUnixDomainTransport(m_tb_control_ipc_file);
766 socket = sts.createTransport(null, 0, m_tb_control_host,
767 m_tb_control_port, null);
770 // If we don't get a response from the control port in 2 seconds, someting is wrong..
771 socket.setTimeout(Ci.nsISocketTransport.TIMEOUT_READ_WRITE, 2);
773 var input = socket.openInputStream(3, 1, 1); // 3 == OPEN_BLOCKING|OPEN_UNBUFFERED
774 var output = socket.openOutputStream(3, 1, 1); // 3 == OPEN_BLOCKING|OPEN_UNBUFFERED
776 var inputStream = Cc["@mozilla.org/binaryinputstream;1"].createInstance(Ci.nsIBinaryInputStream);
777 var outputStream = Cc["@mozilla.org/binaryoutputstream;1"].createInstance(Ci.nsIBinaryOutputStream);
779 inputStream.setInputStream(input);
780 outputStream.setOutputStream(output);
782 var auth_cmd = "AUTHENTICATE "+m_tb_control_pass+"\r\n";
783 outputStream.writeBytes(auth_cmd, auth_cmd.length);
785 var bytes = torbutton_socket_readline(inputStream);
787 if (bytes.indexOf("250") != 0) {
788 torbutton_safelog(4, "Unexpected auth response on control port "+m_tb_control_desc+":", bytes);
792 outputStream.writeBytes(command, command.length);
793 bytes = torbutton_socket_readline(inputStream);
794 if(bytes.indexOf("250") != 0) {
795 torbutton_safelog(4, "Unexpected command response on control port "+m_tb_control_desc+":", bytes);
799 // Closing these streams prevents a shutdown hang on Mac OS. See bug 10201.
801 outputStream.close();
802 socket.close(Cr.NS_OK);
803 return bytes.substr(4);
805 torbutton_log(4, "Exception on control port "+e);
810 // Bug 1506 P4: Needed for New IP Address
811 function torbutton_new_circuit() {
812 let firstPartyDomain = gBrowser.contentPrincipal.originAttributes
815 let domainIsolator = Cc["@torproject.org/domain-isolator;1"]
816 .getService(Ci.nsISupports).wrappedJSObject;
818 domainIsolator.newCircuitForDomain(firstPartyDomain);
820 gBrowser.reloadWithFlags(Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE);
823 // Bug 1506 P4: Needed for New Identity.
824 function torbutton_new_identity() {
826 // Make sure that we can only click once on New Identiy to avoid race
827 // conditions leading to failures (see bug 11783 for an example).
828 // TODO: Remove the Torbutton menu entry again once we have done our
829 // security control redesign.
830 document.getElementById("torbutton-new-identity").disabled = true;
831 document.getElementById("menu_newIdentity").disabled = true;
832 document.getElementById("appMenuNewIdentity").disabled = true;
834 let shouldConfirm = m_tb_prefs.getBoolPref("extensions.torbutton.confirm_newnym");
837 let prompts = Cc["@mozilla.org/embedcomp/prompt-service;1"]
838 .getService(Ci.nsIPromptService);
840 // Display two buttons, both with string titles.
841 let flags = prompts.STD_YES_NO_BUTTONS;
843 let message = torbutton_get_property_string("torbutton.popup.confirm_newnym");
844 let askAgainText = torbutton_get_property_string("torbutton.popup.never_ask_again");
845 let askAgain = {value: false};
847 let confirmed = (prompts.confirmEx(null, "", message, flags, null, null, null,
848 askAgainText, askAgain) == 0);
850 m_tb_prefs.setBoolPref("extensions.torbutton.confirm_newnym", !askAgain.value);
853 torbutton_do_new_identity();
855 // TODO: Remove the Torbutton menu entry again once we have done our
856 // security control redesign.
857 document.getElementById("torbutton-new-identity").disabled = false;
858 document.getElementById("menu_newIdentity").disabled = false;
859 document.getElementById("appMenuNewIdentity").disabled = false;
862 torbutton_do_new_identity();
865 // If something went wrong make sure we have the New Identity button
867 // TODO: Remove the Torbutton menu entry again once we have done our
868 // security control redesign.
869 document.getElementById("torbutton-new-identity").disabled = false;
870 document.getElementById("menu_newIdentity").disabled = false;
871 document.getElementById("appMenuNewIdentity").disabled = false;
872 torbutton_log(5, "Unexpected error on new identity: "+e);
873 window.alert("Torbutton: Unexpected error on new identity: "+e);
877 /* The "New Identity" implementation does the following:
878 * 1. Disables Javascript and plugins on all tabs
881 * b. Cache + image cache
882 * c. Site-specific zoom
883 * d. Cookies+DOM Storage+safe browsing key
884 * e. google wifi geolocation token
887 * h. last open location url
888 * i. clear content prefs
890 * k. site security settings (e.g. HSTS)
891 * l. IndexedDB and asmjscache storage
892 * 3. Sends tor the NEWNYM signal to get a new circuit
893 * 4. Opens a new window with the default homepage
894 * 5. Closes this window
896 * XXX: intermediate SSL certificates are not cleared.
898 // Bug 1506 P4: Needed for New Identity.
899 function torbutton_do_new_identity() {
900 var obsSvc = Components.classes["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
901 torbutton_log(3, "New Identity: Disabling JS");
902 torbutton_disable_all_js();
904 m_tb_prefs.setBoolPref("browser.zoom.siteSpecific",
905 !m_tb_prefs.getBoolPref("browser.zoom.siteSpecific"));
906 m_tb_prefs.setBoolPref("browser.zoom.siteSpecific",
907 !m_tb_prefs.getBoolPref("browser.zoom.siteSpecific"));
910 if(m_tb_prefs.prefHasUserValue("geo.wifi.access_token")) {
911 m_tb_prefs.clearUserPref("geo.wifi.access_token");
914 torbutton_log(3, "Exception on wifi token clear: "+e);
918 if(m_tb_prefs.prefHasUserValue("general.open_location.last_url")) {
919 m_tb_prefs.clearUserPref("general.open_location.last_url");
922 torbutton_log(3, "Exception on clearing last opened location: "+e);
925 torbutton_log(3, "New Identity: Closing tabs and clearing searchbox");
927 torbutton_close_tabs_on_new_identity();
929 // Bug #10800: Trying to clear search/find can cause exceptions
930 // in unknown cases. Just log for now.
932 var searchBar = window.document.getElementById("searchbar");
934 searchBar.textbox.reset();
936 torbutton_log(5, "New Identity: Exception on clearing search box: "+e);
940 if (gFindBarInitialized) {
941 var findbox = gFindBar.getElement("findbar-textbox");
946 torbutton_log(5, "New Identity: Exception on clearing find bar: "+e);
949 torbutton_log(3, "New Identity: Emitting Private Browsing Session clear event");
950 obsSvc.notifyObservers(null, "browser:purge-session-history", "");
952 torbutton_log(3, "New Identity: Clearing HTTP Auth");
954 if(m_tb_prefs.getBoolPref('extensions.torbutton.clear_http_auth')) {
955 var auth = Components.classes["@mozilla.org/network/http-auth-manager;1"].
956 getService(Components.interfaces.nsIHttpAuthManager);
960 torbutton_log(3, "New Identity: Clearing Crypto Tokens");
962 // Clear all crypto auth tokens. This includes calls to PK11_LogoutAll(),
963 // nsNSSComponent::LogoutAuthenticatedPK11() and clearing the SSL session
965 let sdr = Components.classes["@mozilla.org/security/sdr;1"].
966 getService(Components.interfaces.nsISecretDecoderRing);
967 sdr.logoutAndTeardown();
969 // This clears the OCSP cache.
971 // nsNSSComponent::Observe() watches security.OCSP.enabled, which calls
972 // setValidationOptions(), which in turn calls setNonPkixOcspEnabled() which,
973 // if security.OCSP.enabled is set to 0, calls CERT_DisableOCSPChecking(),
974 // which calls CERT_ClearOCSPCache().
975 // See: https://mxr.mozilla.org/comm-esr24/source/mozilla/security/manager/ssl/src/nsNSSComponent.cpp
976 var ocsp = m_tb_prefs.getIntPref("security.OCSP.enabled");
977 m_tb_prefs.setIntPref("security.OCSP.enabled", 0);
978 m_tb_prefs.setIntPref("security.OCSP.enabled", ocsp);
980 // This clears the site permissions on Tor Browser
981 // XXX: Tie to some kind of disk-ok pref?
983 Services.perms.removeAll();
985 // Actually, this catch does not appear to be needed. Leaving it in for
987 torbutton_log(3, "Can't clear permissions: Not Tor Browser: "+e);
990 // Clear site security settings
991 let sss = Cc["@mozilla.org/ssservice;1"].
992 getService(Ci.nsISiteSecurityService);
995 // This clears the undo tab history.
996 var tabs = m_tb_prefs.getIntPref("browser.sessionstore.max_tabs_undo");
997 m_tb_prefs.setIntPref("browser.sessionstore.max_tabs_undo", 0);
998 m_tb_prefs.setIntPref("browser.sessionstore.max_tabs_undo", tabs);
1000 torbutton_log(3, "New Identity: Clearing Image Cache");
1001 torbutton_clear_image_caches();
1003 torbutton_log(3, "New Identity: Clearing Offline Cache");
1006 const LoadContextInfo = Cc["@mozilla.org/load-context-info-factory;1"]
1007 .getService(Ci.nsILoadContextInfoFactory);
1009 for (let contextInfo of [LoadContextInfo.default, LoadContextInfo.private]) {
1010 let appCacheStorage = Services.cache2.appCacheStorage(contextInfo, null);
1011 // The following call (asyncEvictStorage) is actually synchronous, either
1012 // if we have pref "browser.cache.use_new_backend" -> 1 or
1013 // "browser.cache.use_new_backend_temp" -> true,
1014 // then we are using the new cache (cache2) which operates synchronously.
1015 // If we are using the old cache, then the tor-browser.git patch for
1016 // #5715 also makes this synchronous. So we pass a null callback.
1018 appCacheStorage.asyncEvictStorage(null);
1020 // We ignore "not available" errors because they occur if a cache
1021 // has not been used, e.g., if no browsing has been done.
1022 if (err.name !== 'NS_ERROR_NOT_AVAILABLE') {
1028 torbutton_log(5, "Exception on cache clearing: "+e);
1029 window.alert("Torbutton: Unexpected error during offline cache clearing: "+e);
1032 torbutton_log(3, "New Identity: Clearing Disk and Memory Caches");
1035 Services.cache2.clear();
1037 torbutton_log(5, "Exception on cache clearing: "+e);
1038 window.alert("Torbutton: Unexpected error during cache clearing: "+e);
1041 torbutton_log(3, "New Identity: Clearing storage");
1043 let orig_quota_test = m_tb_prefs.getBoolPref("dom.quotaManager.testing");
1045 // This works only by setting the pref to `true` otherwise we get an
1046 // exception and nothing is happening.
1047 m_tb_prefs.setBoolPref("dom.quotaManager.testing", true);
1048 Cc["@mozilla.org/dom/quota-manager-service;1"]
1049 .getService(Ci.nsIQuotaManagerService).clear();
1051 torbutton_log(5, "Exception on storage clearing: "+e);
1053 m_tb_prefs.setBoolPref("dom.quotaManager.testing", orig_quota_test);
1056 torbutton_log(3, "New Identity: Clearing Cookies and DOM Storage");
1058 if (m_tb_prefs.getBoolPref('extensions.torbutton.cookie_protections')) {
1059 var selector = Components.classes["@torproject.org/cookie-jar-selector;1"]
1060 .getService(Components.interfaces.nsISupports)
1062 // This emits "cookie-changed", "cleared", which kills DOM storage
1063 // and the safe browsing API key
1064 selector.clearUnprotectedCookies("tor");
1066 torbutton_clear_cookies();
1069 torbutton_log(3, "New Identity: Closing open connections");
1072 obsSvc.notifyObservers(this, "net:prune-all-connections", null);
1074 torbutton_log(3, "New Identity: Clearing Content Preferences");
1076 // XXX: This may not clear zoom site-specific
1077 // browser.content.full-zoom
1078 if (Ci.nsIContentPrefService2) { // Firefox >= 20
1079 XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
1080 "resource://gre/modules/PrivateBrowsingUtils.jsm");
1081 var pbCtxt = PrivateBrowsingUtils.privacyContextFromWindow(window);
1082 var cps = Cc["@mozilla.org/content-pref/service;1"]
1083 .getService(Ci.nsIContentPrefService2);
1084 cps.removeAllDomains(pbCtxt);
1085 } else { // Firefox < 20
1086 var cps = Cc["@mozilla.org/content-pref/service;1"].
1087 createInstance(Ci.nsIContentPrefService);
1088 cps.removeGroupedPrefs();
1091 torbutton_log(3, "New Identity: Syncing prefs");
1093 // Force prefs to be synced to disk
1094 Services.prefs.savePrefFile(null);
1096 torbutton_log(3, "New Identity: Clearing permissions");
1098 let pm = Cc["@mozilla.org/permissionmanager;1"].
1099 getService(Ci.nsIPermissionManager);
1102 // Clear the domain isolation state.
1103 torbutton_log(3, "New Identity: Clearing domain isolator");
1105 let domainIsolator = Cc["@torproject.org/domain-isolator;1"]
1106 .getService(Ci.nsISupports).wrappedJSObject;
1107 domainIsolator.clearIsolation();
1109 torbutton_log(3, "New Identity: Sending NEWNYM");
1111 // We only support TBB for newnym.
1112 if (!m_tb_control_pass || (!m_tb_control_ipc_file && !m_tb_control_port)) {
1113 var warning = torbutton_get_property_string("torbutton.popup.no_newnym");
1114 torbutton_log(5, "Torbutton cannot safely newnym. It does not have access to the Tor Control Port.");
1115 window.alert(warning);
1117 if (!torbutton_send_ctrl_cmd("SIGNAL NEWNYM\r\n")) {
1118 var warning = torbutton_get_property_string("torbutton.popup.no_newnym");
1119 torbutton_log(5, "Torbutton was unable to request a new circuit from Tor");
1120 window.alert(warning);
1124 torbutton_log(3, "Ending any remaining private browsing sessions.");
1125 obsSvc.notifyObservers(null, "last-pb-context-exited", "");
1127 torbutton_log(3, "New Identity: Opening a new browser window");
1129 // Open a new window with the TBB check homepage
1130 // In Firefox >=19, can pass {private: true} but we do not need it because
1131 // we have browser.privatebrowsing.autostart = true
1132 OpenBrowserWindow();
1134 torbutton_log(3, "New identity successful");
1136 // Run garbage collection and cycle collection after window is gone.
1137 // This ensures that blob URIs are forgotten.
1138 window.addEventListener("unload", function (event) {
1139 torbutton_log(3, "Initiating New Identity GC pass");
1140 // Clear out potential pending sInterSliceGCTimer:
1141 m_tb_domWindowUtils.runNextCollectorTimer();
1143 // Clear out potential pending sICCTimer:
1144 m_tb_domWindowUtils.runNextCollectorTimer();
1146 // Schedule a garbage collection in 4000-1000ms...
1147 m_tb_domWindowUtils.garbageCollect();
1149 // To ensure the GC runs immediately instead of 4-10s from now, we need
1150 // to poke it at least 11 times.
1151 // We need 5 pokes for GC, 1 poke for the interSliceGC, and 5 pokes for CC.
1152 // See nsJSContext::RunNextCollectorTimer() in
1153 // https://mxr.mozilla.org/mozilla-central/source/dom/base/nsJSEnvironment.cpp#1970.
1154 // XXX: We might want to make our own method for immediate full GC...
1155 for (let poke = 0; poke < 11; poke++) {
1156 m_tb_domWindowUtils.runNextCollectorTimer();
1159 // And now, since the GC probably actually ran *after* the CC last time,
1160 // run the whole thing again.
1161 m_tb_domWindowUtils.garbageCollect();
1162 for (let poke = 0; poke < 11; poke++) {
1163 m_tb_domWindowUtils.runNextCollectorTimer();
1166 torbutton_log(3, "Completed New Identity GC pass");
1169 // Close the current window for added safety
1173 function torbutton_clear_image_caches()
1177 let imgTools = Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools);
1178 if (!("getImgCacheForDocument" in imgTools)) {
1179 // In Firefox 17 and older, there is one global image cache. Clear it.
1180 imgCache = Cc["@mozilla.org/image/cache;1"].getService(Ci.imgICache);
1181 imgCache.clearCache(false); // evict all but chrome cache
1183 // In Firefox 18 and newer, there are two image caches: one that is
1184 // used for regular browsing and one that is used for private browsing.
1186 // Clear the non-private browsing image cache.
1187 imgCache = imgTools.getImgCacheForDocument(null);
1188 imgCache.clearCache(false); // evict all but chrome cache
1190 // Try to clear the private browsing cache. To do so, we must locate
1191 // a content document that is contained within a private browsing window.
1192 let didClearPBCache = false;
1193 let wm = Cc["@mozilla.org/appshell/window-mediator;1"]
1194 .getService(Ci.nsIWindowMediator);
1195 let enumerator = wm.getEnumerator("navigator:browser");
1196 while (!didClearPBCache && enumerator.hasMoreElements()) {
1197 let win = enumerator.getNext();
1198 let browserDoc = win.document.documentElement;
1199 if (!browserDoc.hasAttribute("privatebrowsingmode"))
1202 let tabbrowser = win.getBrowser();
1206 var tabCount = tabbrowser.browsers.length;
1207 for (var i = 0; i < tabCount; i++) {
1208 let doc = tabbrowser.browsers[i].contentDocument;
1210 imgCache = imgTools.getImgCacheForDocument(doc);
1211 imgCache.clearCache(false); // evict all but chrome cache
1212 didClearPBCache = true;
1219 // FIXME: This can happen in some rare cases involving XULish image data
1220 // in combination with our image cache isolation patch. Sure isn't
1221 // a good thing, but it's not really a super-cookie vector either.
1222 // We should fix it eventually.
1223 torbutton_log(4, "Exception on image cache clearing: "+e);
1227 /* Called when we switch the use_nontor_proxy pref in either direction.
1229 * Enables/disables domain isolation and then does new identity
1231 function torbutton_use_nontor_proxy()
1233 let domainIsolator = Cc["@torproject.org/domain-isolator;1"]
1234 .getService(Ci.nsISupports).wrappedJSObject;
1236 if (m_tb_prefs.getBoolPref("extensions.torbutton.use_nontor_proxy")) {
1237 // Disable domain isolation
1238 domainIsolator.disableIsolation();
1240 domainIsolator.enableIsolation();
1243 // Always reset our identity if the proxy has changed from tor
1245 torbutton_do_new_identity();
1248 function torbutton_do_tor_check()
1250 let checkSvc = Cc["@torproject.org/torbutton-torCheckService;1"]
1251 .getService(Ci.nsISupports).wrappedJSObject;
1252 if (checkSvc.kCheckNotInitiated != checkSvc.statusOfTorCheck ||
1253 m_tb_prefs.getBoolPref("extensions.torbutton.use_nontor_proxy") ||
1254 !m_tb_prefs.getBoolPref("extensions.torbutton.test_enabled"))
1255 return; // Only do the check once.
1257 // If we have a tor control port and transparent torification is off,
1258 // perform a check via the control port.
1259 const kEnvSkipControlPortTest = "TOR_SKIP_CONTROLPORTTEST";
1260 const kEnvUseTransparentProxy = "TOR_TRANSPROXY";
1261 var env = Cc["@mozilla.org/process/environment;1"]
1262 .getService(Ci.nsIEnvironment);
1263 if ((m_tb_control_ipc_file || m_tb_control_port) &&
1264 !env.exists(kEnvUseTransparentProxy) &&
1265 !env.exists(kEnvSkipControlPortTest) &&
1266 m_tb_prefs.getBoolPref("extensions.torbutton.local_tor_check")) {
1267 if (torbutton_local_tor_check())
1268 checkSvc.statusOfTorCheck = checkSvc.kCheckSuccessful;
1270 // The check failed. Update toolbar icon and tooltip.
1271 checkSvc.statusOfTorCheck = checkSvc.kCheckFailed;
1272 torbutton_update_toolbutton();
1276 // A local check is not possible, so perform a remote check.
1277 torbutton_initiate_remote_tor_check();
1281 function torbutton_local_tor_check()
1283 let didLogError = false;
1285 let proxyType = m_tb_prefs.getIntPref("network.proxy.type");
1289 // Ask tor for its SOCKS listener address and port and compare to the
1290 // browser preferences.
1291 const kCmdArg = "net/listeners/socks";
1292 let resp = torbutton_send_ctrl_cmd("GETINFO " + kCmdArg + "\r\n");
1296 function logUnexpectedResponse()
1300 torbutton_log(5, "Local Tor check: unexpected GETINFO response: " + resp);
1304 function removeBrackets(aStr)
1306 // Remove enclosing square brackets if present.
1307 if (aStr.startsWith('[') && aStr.endsWith(']'))
1308 return aStr.substr(1, aStr.length - 2);
1313 // Sample response: net/listeners/socks="127.0.0.1:9149" "127.0.0.1:9150"
1314 // First, check for and remove the command argument prefix.
1315 if (0 != resp.indexOf(kCmdArg + '=')) {
1316 logUnexpectedResponse();
1319 resp = resp.substr(kCmdArg.length + 1);
1321 // Retrieve configured proxy settings and check each listener against them.
1322 // When the SOCKS prefs are set to use IPC (e.g., a Unix domain socket), a
1323 // file URL should be present in network.proxy.socks.
1324 // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1211567
1325 let socksAddr = m_tb_prefs.getCharPref("network.proxy.socks");
1326 let socksPort = m_tb_prefs.getIntPref("network.proxy.socks_port");
1328 if (socksAddr && socksAddr.startsWith("file:")) {
1329 // Convert the file URL to a file path.
1331 let ioService = Cc["@mozilla.org/network/io-service;1"]
1332 .getService(Ci.nsIIOService);
1333 let fph = ioService.getProtocolHandler("file")
1334 .QueryInterface(Ci.nsIFileProtocolHandler);
1335 socksIPCPath = fph.getFileFromURLSpec(socksAddr).path;
1337 torbutton_log(5, "Local Tor check: IPC file error: " + e);
1341 socksAddr = removeBrackets(socksAddr);
1344 // Split into quoted strings. This code is adapted from utils.splitAtSpaces()
1345 // within tor-control-port.js; someday this code should use the entire
1346 // tor-control-port.js framework.
1348 resp.replace(/((\S*?"(.*?)")+\S*|\S+)/g, function (a, captured) {
1349 addrArray.push(captured);
1352 let foundSocksListener = false;
1353 for (let i = 0; !foundSocksListener && (i < addrArray.length); ++i) {
1355 try { addr = unescapeTorString(addrArray[i]); } catch (e) {}
1359 // Remove double quotes if present.
1360 let len = addr.length;
1361 if ((len > 2) && ('"' == addr.charAt(0)) && ('"' == addr.charAt(len - 1)))
1362 addr = addr.substring(1, len - 1);
1364 if (addr.startsWith("unix:")) {
1368 // Check against the configured UNIX domain socket proxy.
1369 let path = addr.substring(5);
1370 torbutton_log(2, "Tor socks listener (Unix domain socket): " + path);
1371 foundSocksListener = (socksIPCPath === path);
1372 } else if (!socksIPCPath) {
1373 // Check against the configured TCP proxy. We expect addr:port where addr
1374 // may be an IPv6 address; that is, it may contain colon characters.
1375 // Also, we remove enclosing square brackets before comparing addresses
1376 // because tor requires them but Firefox does not.
1377 let idx = addr.lastIndexOf(':');
1379 logUnexpectedResponse();
1381 let torSocksAddr = removeBrackets(addr.substring(0, idx));
1382 let torSocksPort = parseInt(addr.substring(idx + 1), 10);
1383 if ((torSocksAddr.length < 1) || isNaN(torSocksPort)) {
1384 logUnexpectedResponse();
1386 torbutton_log(2, "Tor socks listener: " + torSocksAddr + ':'
1388 foundSocksListener = ((socksAddr === torSocksAddr) &&
1389 (socksPort === torSocksPort));
1395 return foundSocksListener;
1396 } // torbutton_local_tor_check
1399 function torbutton_initiate_remote_tor_check()
1401 let obsSvc = Cc["@mozilla.org/observer-service;1"]
1402 .getService(Ci.nsIObserverService);
1405 let checkSvc = Cc["@torproject.org/torbutton-torCheckService;1"]
1406 .getService(Ci.nsISupports).wrappedJSObject;
1407 let req = checkSvc.createCheckRequest(true); // async
1408 req.onreadystatechange = function (aEvent) {
1409 if (req.readyState === 4) {
1410 let ret = checkSvc.parseCheckResponse(req);
1412 // If we received an error response from check.torproject.org,
1413 // set the status of the tor check to failure (we don't want
1414 // to indicate failure if we didn't receive a response).
1415 if (ret == 2 || ret == 3 || ret == 5 || ret == 6
1416 || ret == 7 || ret == 8) {
1417 checkSvc.statusOfTorCheck = checkSvc.kCheckFailed;
1418 obsSvc.notifyObservers(null, k_tb_tor_check_failed_topic, null);
1419 } else if (ret == 4) {
1420 checkSvc.statusOfTorCheck = checkSvc.kCheckSuccessful;
1421 } // Otherwise, redo the check later
1423 torbutton_log(3, "Tor remote check done. Result: " + ret);
1427 torbutton_log(3, "Sending async Tor remote check");
1430 if (e.result == 0x80004005) // NS_ERROR_FAILURE
1431 torbutton_log(5, "Tor check failed! Is tor running?");
1433 torbutton_log(5, "Tor check failed! Tor internal error: "+e);
1435 checkSvc.statusOfTorCheck = checkSvc.kCheckFailed;
1436 obsSvc.notifyObservers(null, k_tb_tor_check_failed_topic, null);
1438 } // torbutton_initiate_remote_tor_check()
1440 function torbutton_tor_check_ok()
1442 let checkSvc = Cc["@torproject.org/torbutton-torCheckService;1"]
1443 .getService(Ci.nsISupports).wrappedJSObject;
1444 return (checkSvc.kCheckFailed != checkSvc.statusOfTorCheck);
1447 // Bug 1506 P5: Despite the name, this is the way we disable
1448 // plugins for Tor Browser, too.
1450 // toggles plugins: true for disabled, false for enabled
1451 function torbutton_toggle_plugins(disable_plugins) {
1453 var PH=Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
1454 var P=PH.getPluginTags({});
1455 for(var i=0; i<P.length; i++) {
1456 if ("enabledState" in P[i]) { // FF24
1457 // FIXME: DOCDOC the reasoning for the isDisabled check, or remove it.
1458 var isDisabled = (P[i].enabledState == Ci.nsIPluginTag.STATE_DISABLED);
1459 if (!isDisabled && disable_plugins)
1460 P[i].enabledState = Ci.nsIPluginTag.STATE_DISABLED;
1461 else if (isDisabled && !disable_plugins)
1462 P[i].enabledState = Ci.nsIPluginTag.STATE_CLICKTOPLAY;
1463 } else if (P[i].disabled != disable_plugins) { // FF17
1464 P[i].disabled=disable_plugins;
1470 function torbutton_update_disk_prefs() {
1471 var mode = m_tb_prefs.getBoolPref("browser.privatebrowsing.autostart");
1473 m_tb_prefs.setBoolPref("browser.cache.disk.enable", !mode);
1474 m_tb_prefs.setBoolPref("places.history.enabled", !mode);
1476 m_tb_prefs.setBoolPref("security.nocertdb", mode);
1478 // No way to clear this beast during New Identity. Leave it off.
1479 //m_tb_prefs.setBoolPref("dom.indexedDB.enabled", !mode);
1481 if (m_tb_tbb) m_tb_prefs.setBoolPref("permissions.memory_only", mode);
1483 // Third party abuse. Leave it off for now.
1484 //m_tb_prefs.setBoolPref("browser.cache.offline.enable", !mode);
1487 m_tb_prefs.setIntPref("network.cookie.lifetimePolicy", 2);
1488 m_tb_prefs.setIntPref("browser.download.manager.retention", 1);
1490 m_tb_prefs.setIntPref("network.cookie.lifetimePolicy", 0);
1491 m_tb_prefs.setIntPref("browser.download.manager.retention", 2);
1494 // Force prefs to be synced to disk
1495 Services.prefs.savePrefFile(null);
1498 function torbutton_update_fingerprinting_prefs() {
1499 var mode = m_tb_prefs.getBoolPref("privacy.resistFingerprinting");
1501 m_tb_prefs.setBoolPref("webgl.disable-extensions", mode);
1502 m_tb_prefs.setBoolPref("dom.network.enabled", !mode);
1503 m_tb_prefs.setBoolPref("dom.enable_performance", !mode);
1504 m_tb_prefs.setBoolPref("plugin.expose_full_path", !mode);
1505 m_tb_prefs.setBoolPref("browser.zoom.siteSpecific", !mode);
1507 m_tb_prefs.setBoolPref("extensions.torbutton.resize_new_windows", mode);
1509 // Force prefs to be synced to disk
1510 Services.prefs.savePrefFile(null);
1513 function torbutton_update_isolation_prefs() {
1514 let isolate = m_tb_prefs.getBoolPref("privacy.firstparty.isolate");
1517 m_tb_prefs.setIntPref("network.cookie.cookieBehavior", 1);
1519 m_tb_prefs.setIntPref("network.cookie.cookieBehavior", 0);
1522 m_tb_prefs.setBoolPref("security.enable_tls_session_tickets", !isolate);
1524 // Force prefs to be synced to disk
1525 Services.prefs.savePrefFile(null);
1528 // This function closes all XUL browser windows except this one. For this
1529 // window, it closes all existing tabs and creates one about:blank tab.
1530 function torbutton_close_tabs_on_new_identity() {
1531 if (!m_tb_prefs.getBoolPref("extensions.torbutton.close_newnym")) {
1532 torbutton_log(3, "Not closing tabs");
1536 // TODO: muck around with browser.tabs.warnOnClose.. maybe..
1537 torbutton_log(3, "Closing tabs...");
1538 let wm = Cc["@mozilla.org/appshell/window-mediator;1"]
1539 .getService(Ci.nsIWindowMediator);
1540 let enumerator = wm.getEnumerator("navigator:browser");
1541 let windowsToClose = new Array();
1542 while (enumerator.hasMoreElements()) {
1543 let win = enumerator.getNext();
1544 let browser = win.getBrowser();
1546 torbutton_log(5, "No browser for possible closed window");
1550 let tabCount = browser.browsers.length;
1551 torbutton_log(3, "Tab count for window: " + tabCount);
1552 let tabsToRemove = new Array();
1553 for (let i = 0; i < tabCount; i++) {
1554 let tab = browser.getTabForBrowser(browser.browsers[i]);
1556 torbutton_log(5, "No tab for browser");
1558 tabsToRemove.push(tab);
1562 if (win == window) {
1563 browser.addTab("about:blank");
1565 // It is a bad idea to alter the window list while iterating
1566 // over it, so add this window to an array and close it later.
1567 windowsToClose.push(win);
1570 // Close each tab except the new blank one that we created.
1571 tabsToRemove.forEach(aTab => browser.removeTab(aTab));
1574 // Close all XUL windows except this one.
1575 torbutton_log(2, "Closing windows...");
1576 windowsToClose.forEach(aWin => aWin.close());
1578 torbutton_log(3, "Closed all tabs");
1581 // Bug 1506 P2: This code is only important for disabling
1582 // New Identity where it is not supported (ie no control port).
1583 function torbutton_check_protections()
1585 var env = Cc["@mozilla.org/process/environment;1"]
1586 .getService(Ci.nsIEnvironment);
1588 // Bug 14100: check for the existence of an environment variable
1589 // in order to toggle the visibility of networksettings menuitem
1590 if (env.exists("TOR_NO_DISPLAY_NETWORK_SETTINGS"))
1591 document.getElementById("torbutton-networksettings").hidden = true;
1593 document.getElementById("torbutton-networksettings").hidden = false;
1595 // Bug 21091: check for the existence of an environment variable
1596 // in order to toggle the visibility of the torbutton-checkForUpdate
1597 // menuitem and its separator.
1598 if (env.exists("TOR_HIDE_UPDATE_CHECK_UI")) {
1599 document.getElementById("torbutton-checkForUpdateSeparator").hidden = true;
1600 document.getElementById("torbutton-checkForUpdate").hidden = true;
1602 document.getElementById("torbutton-checkForUpdateSeparator").hidden = false;
1603 document.getElementById("torbutton-checkForUpdate").hidden = false;
1606 var cookie_pref = m_tb_prefs.getBoolPref("extensions.torbutton.cookie_protections");
1607 document.getElementById("torbutton-cookie-protector").disabled = !cookie_pref;
1609 // XXX: Bug 14632: The cookie dialog is useless in private browsing mode in FF31ESR
1610 // See https://trac.torproject.org/projects/tor/ticket/10353 for more info.
1611 document.getElementById("torbutton-cookie-protector").hidden = m_tb_prefs.getBoolPref("browser.privatebrowsing.autostart");
1613 if (!m_tb_control_pass || (!m_tb_control_ipc_file && !m_tb_control_port)) {
1614 // TODO: Remove the Torbutton menu entry again once we have done our
1615 // security control redesign.
1616 document.getElementById("torbutton-new-identity").disabled = true;
1617 document.getElementById("menu_newIdentity").disabled = true;
1618 document.getElementById("appMenuNewIdentity").disabled = true;
1621 if (!m_tb_tbb && m_tb_prefs.getBoolPref("extensions.torbutton.prompt_torbrowser")) {
1622 torbutton_inform_about_tbb();
1626 // Bug 1506 P2: I think cookie protections is a neat feature.
1627 function torbutton_open_cookie_dialog() {
1628 showDialog(window, 'chrome://torbutton/content/torcookiedialog.xul',
1629 'Cookie Protections', 'centerscreen,chrome,dialog,modal,resizable');
1632 // Bug 1506 P2/P3: Prefs are handled differently on android, I guess?
1633 function torbutton_open_prefs_dialog() {
1634 showDialog(window, "chrome://torbutton/content/preferences.xul",
1635 "torbutton-preferences","centerscreen, chrome");
1636 torbutton_log(2, 'opened preferences window');
1639 // -------------- HISTORY & COOKIES ---------------------
1641 // Bug 1506 P4: Used by New Identity if cookie protections are
1643 function torbutton_clear_cookies() {
1644 torbutton_log(2, 'called torbutton_clear_cookies');
1645 var cm = Components.classes["@mozilla.org/cookiemanager;1"]
1646 .getService(Components.interfaces.nsICookieManager);
1651 // -------------- JS/PLUGIN HANDLING CODE ---------------------
1652 // Bug 1506 P3: Defense in depth. Disables JS and events for New Identity.
1653 function torbutton_disable_browser_js(browser) {
1654 var eventSuppressor = null;
1656 /* Solution from: https://bugzilla.mozilla.org/show_bug.cgi?id=409737 */
1657 // XXX: This kills the entire window. We need to redirect
1658 // focus and inform the user via a lightbox.
1660 if (!browser.contentWindow)
1661 torbutton_log(3, "No content window to disable JS events.");
1663 eventSuppressor = browser.contentWindow.
1664 QueryInterface(Components.interfaces.nsIInterfaceRequestor).
1665 getInterface(Ci.nsIDOMWindowUtils);
1667 torbutton_log(4, "Failed to disable JS events: "+e)
1670 if (browser.docShell)
1671 browser.docShell.allowJavascript = false;
1674 // My estimation is that this does not get the inner iframe windows,
1675 // but that does not matter, because iframes should be destroyed
1676 // on the next load.
1677 browser.contentWindow.name = null;
1678 browser.contentWindow.window.name = null;
1680 torbutton_log(4, "Failed to reset window.name: "+e)
1683 if (eventSuppressor)
1684 eventSuppressor.suppressEventHandling(true);
1687 // Bug 1506 P3: The JS-killing bits of this are used by
1688 // New Identity as a defense-in-depth measure.
1689 function torbutton_disable_window_js(win) {
1690 var browser = win.getBrowser();
1692 torbutton_log(5, "No browser for plugin window...");
1695 var browsers = browser.browsers;
1696 torbutton_log(1, "Toggle window plugins");
1698 for (var i = 0; i < browsers.length; ++i) {
1699 var b = browser.browsers[i];
1700 if (b && !b.docShell) {
1703 torbutton_log(5, "DocShell is null for: "+b.currentURI.spec);
1705 torbutton_log(5, "DocShell is null for unknown URL");
1707 torbutton_log(5, "DocShell is null for unparsable URL: "+e);
1710 if (b && b.docShell) {
1711 torbutton_disable_browser_js(b);
1713 // kill meta-refresh and existing page loading
1714 // XXX: Despite having JUST checked b.docShell, it can
1715 // actually end up NULL here in some cases?
1717 if (b.docShell && b.webNavigation)
1718 b.webNavigation.stop(b.webNavigation.STOP_ALL);
1720 torbutton_log(4, "DocShell error: "+e);
1726 // Bug 1506 P3: The JS-killing bits of this are used by
1727 // New Identity as a defense-in-depth measure.
1729 // This is an ugly beast.. But unfortunately it has to be so..
1730 // Looping over all tabs twice is not somethign we wanna do..
1731 function torbutton_disable_all_js() {
1732 var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
1733 .getService(Components.interfaces.nsIWindowMediator);
1734 var enumerator = wm.getEnumerator("navigator:browser");
1735 while(enumerator.hasMoreElements()) {
1736 var win = enumerator.getNext();
1737 torbutton_disable_window_js(win);
1741 // Bug 1506 P1: This function just cleans up prefs that got set badly in previous releases
1742 function torbutton_fixup_old_prefs()
1744 if(m_tb_prefs.getIntPref('extensions.torbutton.pref_fixup_version') < 1) {
1745 // TBB 5.0a3 had bad Firefox code that silently flipped this pref on us
1746 if (m_tb_prefs.prefHasUserValue("browser.newtabpage.enhanced")) {
1747 m_tb_prefs.clearUserPref("browser.newtabpage.enhanced");
1748 // TBB 5.0a3 users had all the necessary data cached in
1749 // directoryLinks.json. This meant that resetting the pref above
1750 // alone was not sufficient as the tiles features uses the cache
1751 // even if the pref indicates that feature should be disabled.
1752 // We flip the preference below as this forces a refetching which
1753 // effectively results in an empty JSON file due to our spoofed
1755 let matchOS = m_tb_prefs.getBoolPref("intl.locale.matchOS");
1756 m_tb_prefs.setBoolPref("intl.locale.matchOS", !matchOS);
1757 m_tb_prefs.setBoolPref("intl.locale.matchOS", matchOS);
1760 // For some reason, the Share This Page button also survived the
1761 // TBB 5.0a4 update's attempt to remove it.
1762 if (m_tb_prefs.prefHasUserValue("browser.uiCustomization.state")) {
1763 m_tb_prefs.clearUserPref("browser.uiCustomization.state");
1766 m_tb_prefs.setIntPref('extensions.torbutton.pref_fixup_version', 1);
1770 // ---------------------- Event handlers -----------------
1772 // Bug 1506 P1-P3: Most of these observers aren't very important.
1773 // See their comments for details
1774 function torbutton_do_main_window_startup()
1776 torbutton_log(3, "Torbutton main window startup");
1777 m_tb_is_main_window = true;
1778 torbutton_unique_pref_observer.register();
1781 // Bug 1506 P4: Most of this function is now useless, save
1782 // for the very important SOCKS environment vars at the end.
1783 // Those could probably be rolled into a function with the
1784 // control port vars, though. See 1506 comments inside.
1785 function torbutton_do_startup()
1787 if(m_tb_prefs.getBoolPref("extensions.torbutton.startup")) {
1788 // Bug 1506: Still want to do this
1789 torbutton_toggle_plugins(
1790 m_tb_prefs.getBoolPref("plugin.disable"));
1792 // Bug 1506: Should probably be moved to an XPCOM component
1793 torbutton_do_main_window_startup();
1796 torbutton_update_fingerprinting_prefs();
1798 // #5758: Last ditch effort to keep Vanilla Torbutton users from totally
1799 // being pwnt. This is a pretty darn ugly hack, too. But because of #5863,
1800 // we really don't care about preserving the user's values for this.
1802 // Bug 1506 P5: You have to set these two for non-TBB Firefoxen
1803 m_tb_prefs.setBoolPref("network.websocket.enabled", false);
1804 m_tb_prefs.setBoolPref("dom.indexedDB.enabled", false);
1807 // Still need this in case people shove this thing back into FF
1808 if (!m_tb_tbb && m_tb_prefs.getBoolPref("extensions.torbutton.prompt_torbrowser")) {
1809 var warning = torbutton_get_property_string("torbutton.popup.short_torbrowser");
1810 var title = torbutton_get_property_string("torbutton.title.prompt_torbrowser");
1811 var prompts = Cc["@mozilla.org/embedcomp/prompt-service;1"].getService(Components.interfaces.nsIPromptService);
1812 prompts.alert(null, title, warning);
1815 // For general pref fixups to handle pref damage in older versions
1816 torbutton_fixup_old_prefs();
1818 m_tb_prefs.setBoolPref("extensions.torbutton.startup", false);
1822 // Perform version check when a new tab is opened.
1823 function torbutton_new_tab(event)
1825 // listening for new tabs
1826 torbutton_log(3, "New tab");
1828 /* Perform the version check on new tab, module timer */
1829 torbutton_do_async_versioncheck();
1832 // Bug 1506 P3: Used to decide if we should resize the window.
1834 // Returns true if the window wind is neither maximized, full screen,
1835 // ratpoisioned/evilwmed, nor minimized.
1836 function torbutton_is_windowed(wind) {
1837 torbutton_log(3, "Window: (" + wind.outerWidth + "," + wind.outerHeight + ") ?= ("
1838 + wind.screen.availWidth + "," + wind.screen.availHeight + ")");
1839 if(wind.windowState == Components.interfaces.nsIDOMChromeWindow.STATE_MINIMIZED
1840 || wind.windowState == Components.interfaces.nsIDOMChromeWindow.STATE_MAXIMIZED) {
1841 torbutton_log(2, "Window is minimized/maximized");
1844 if ("fullScreen" in wind && wind.fullScreen) {
1845 torbutton_log(2, "Window is fullScreen");
1848 if(wind.outerHeight == wind.screen.availHeight
1849 && wind.outerWidth == wind.screen.availWidth) {
1850 torbutton_log(3, "Window is ratpoisoned/evilwm'ed");
1854 torbutton_log(2, "Window is normal");
1858 let stopOpenSecuritySettingsObserver;
1860 function showSecurityPreferencesPanel(chromeWindow) {
1861 const tabBrowser = chromeWindow.BrowserApp;
1862 let settingsTab = null;
1864 const SECURITY_PREFERENCES_URI = 'chrome://torbutton/content/preferences.xhtml';
1866 tabBrowser.tabs.some(function (tab) {
1867 // If the security prefs tab is opened, send the user to it
1868 if (tab.browser.currentURI.spec === SECURITY_PREFERENCES_URI) {
1875 if (settingsTab === null) {
1876 // Open up the settings panel in a new tab.
1877 tabBrowser.addTab(SECURITY_PREFERENCES_URI, {
1879 'parentId': tabBrowser.selectedTab.id
1882 // Activate an existing settings panel tab.
1883 tabBrowser.selectTab(settingsTab);
1887 function setupPreferencesForMobile() {
1888 if (!torbutton_is_mobile()) {
1892 torbutton_log(4, "Setting up settings preferences for Android.");
1894 const chromeWindow = Services.wm.getMostRecentWindow('navigator:browser');
1896 // Add the extension's chrome menu item to the main browser menu.
1897 chromeWindow.NativeWindow.menu.add({
1898 'name': torbutton_get_property_string("torbutton.security_settings.menu.title"),
1899 'callback': showSecurityPreferencesPanel.bind(this, chromeWindow)
1903 // Bug 1506 P3: This is needed pretty much only for the version check
1904 // and the window resizing. See comments for individual functions for
1906 function torbutton_new_window(event)
1908 torbutton_log(3, "New window");
1909 var browser = window.gBrowser;
1912 torbutton_log(5, "No browser for new window.");
1916 m_tb_window_height = window.outerHeight;
1917 m_tb_window_width = window.outerWidth;
1919 if (!m_tb_wasinited) {
1922 // Add tab open listener..
1923 browser.tabContainer.addEventListener("TabOpen", torbutton_new_tab, false);
1925 torbutton_do_startup();
1927 let progress = Cc["@mozilla.org/docloaderservice;1"]
1928 .getService(Ci.nsIWebProgress);
1930 if (m_tb_prefs.getBoolPref("extensions.torbutton.resize_new_windows")
1931 && torbutton_is_windowed(window)) {
1932 progress.addProgressListener(torbutton_resizelistener,
1933 Ci.nsIWebProgress.NOTIFY_STATE_DOCUMENT);
1936 // Register the TorOpenSecuritySettings observer, which is used by
1937 // UITour to open the security slider dialog.
1938 stopOpenSecuritySettingsObserver = observe("TorOpenSecuritySettings",
1939 torbutton_open_prefs_dialog);
1941 // Check the version on every new window. We're already pinging check in these cases.
1942 torbutton_do_async_versioncheck();
1944 torbutton_do_tor_check();
1947 // Bug 1506 P2: This is only needed because we have observers
1948 // in XUL that should be in an XPCOM component
1949 function torbutton_close_window(event) {
1950 torbutton_window_pref_observer.unregister();
1951 torbutton_tor_check_observer.unregister();
1952 stopOpenSecuritySettingsObserver();
1954 window.removeEventListener("sizemodechange", m_tb_resize_handler,
1957 // TODO: This is a real ghetto hack.. When the original window
1958 // closes, we need to find another window to handle observing
1959 // unique events... The right way to do this is to move the
1960 // majority of torbutton functionality into a XPCOM component..
1961 // But that is a major overhaul..
1962 if (m_tb_is_main_window) {
1963 torbutton_log(3, "Original window closed. Searching for another");
1964 var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
1965 .getService(Components.interfaces.nsIWindowMediator);
1966 var enumerator = wm.getEnumerator("navigator:browser");
1967 while(enumerator.hasMoreElements()) {
1968 var win = enumerator.getNext();
1969 // For some reason, when New Identity is called from a pref
1970 // observer (ex: torbutton_use_nontor_proxy) on an ASAN build,
1971 // we sometimes don't have this symbol set in the new window yet.
1972 // However, the new window will run this init later in that case,
1973 // as it does in the OSX case.
1974 if(win != window && "torbutton_do_main_window_startup" in win) {
1975 torbutton_log(3, "Found another window");
1976 win.torbutton_do_main_window_startup();
1977 m_tb_is_main_window = false;
1982 torbutton_unique_pref_observer.unregister();
1984 if(m_tb_is_main_window) { // main window not reset above
1985 // This happens on Mac OS because they allow firefox
1986 // to still persist without a navigator window
1987 torbutton_log(3, "Last window closed. None remain.");
1988 m_tb_prefs.setBoolPref("extensions.torbutton.startup", true);
1989 m_tb_is_main_window = false;
1995 function torbutton_open_network_settings() {
1996 var obsSvc = Components.classes["@mozilla.org/observer-service;1"]
1997 .getService(Ci.nsIObserverService);
1998 obsSvc.notifyObservers(this, "TorOpenNetworkSettings", null);
2002 window.addEventListener('load',torbutton_new_window,false);
2003 window.addEventListener('unload', torbutton_close_window, false);
2005 var m_tb_resize_handler = null;
2006 var m_tb_resize_date = null;
2008 // Bug 1506 P1/P3: Setting a fixed window size is important, but
2009 // probably not for android.
2010 var torbutton_resizelistener =
2012 QueryInterface: function(aIID)
2014 if (aIID.equals(Ci.nsIWebProgressListener) ||
2015 aIID.equals(Ci.nsISupportsWeakReference) ||
2016 aIID.equals(Ci.nsISupports))
2018 throw Cr.NS_NOINTERFACE;
2021 onLocationChange: function(aProgress, aRequest, aURI) {},
2022 onStateChange: function(aProgress, aRequest, aFlag, aStatus) {
2023 if (aFlag & Ci.nsIWebProgressListener.STATE_STOP) {
2024 m_tb_resize_handler = async function() {
2025 // Wait for end of execution queue to ensure we have correct windowState.
2026 await new Promise(resolve => setTimeout(resolve, 0));
2027 if (window.windowState === window.STATE_MAXIMIZED ||
2028 window.windowState === window.STATE_FULLSCREEN) {
2030 getIntPref("extensions.torbutton.maximize_warnings_remaining") > 0) {
2032 // Do not add another notification if one is already showing.
2033 const kNotificationName = "torbutton-maximize-notification";
2034 let box = gBrowser.getNotificationBox();
2035 if (box.getNotificationWithValue(kNotificationName))
2038 // Rate-limit showing our notification if needed.
2039 if (m_tb_resize_date === null) {
2040 m_tb_resize_date = Date.now();
2042 // We wait at least another second before we show a new
2043 // notification. Should be enough to rule out OSes that call our
2044 // handler rapidly due to internal workings.
2045 if (Date.now() - m_tb_resize_date < 1000) {
2048 // Resizing but we need to reset |m_tb_resize_date| now.
2049 m_tb_resize_date = Date.now();
2052 let sb = torbutton_get_stringbundle();
2053 // No need to get "OK" translated again.
2054 let sbSvc = Cc["@mozilla.org/intl/stringbundle;1"].
2055 getService(Ci.nsIStringBundleService);
2057 createBundle("chrome://global/locale/commonDialogs.properties");
2058 let button_label = bundle.GetStringFromName("OK");
2061 label: button_label,
2066 m_tb_prefs.setIntPref("extensions.torbutton.maximize_warnings_remaining",
2067 m_tb_prefs.getIntPref("extensions.torbutton.maximize_warnings_remaining") - 1);
2071 let priority = box.PRIORITY_WARNING_LOW;
2073 torbutton_get_property_string("torbutton.maximize_warning");
2075 box.appendNotification(message, kNotificationName, null,
2080 }; // m_tb_resize_handler
2082 // We need to handle OSes that auto-maximize windows depending on user
2083 // settings and/or screen resolution in the start-up phase and users that
2084 // try to shoot themselves in the foot by maximizing the window manually.
2085 // We add a listener which is triggerred as soon as the window gets
2086 // maximized (windowState = 1). We are resizing during start-up but not
2087 // later as the user should see only a warning there as a stopgap before
2089 // Alas, the Firefox window code is handling the event not itself:
2090 // "// Note the current implementation of SetSizeMode just stores
2091 // // the new state; it doesn't actually resize. So here we store
2092 // // the state and pass the event on to the OS."
2093 // (See: https://mxr.mozilla.org/mozilla-esr31/source/xpfe/appshell/src/
2094 // nsWebShellWindow.cpp#348)
2095 // This means we have to cope with race conditions and resizing in the
2096 // sizemodechange listener is likely to fail. Thus, we add a specific
2097 // resize listener that is doing the work for us. It seems (at least on
2098 // Ubuntu) to be the case that maximizing (and then again normalizing) of
2099 // the window triggers more than one resize event the first being not the
2100 // one we need. Thus we can't remove the listener after the first resize
2101 // event got fired. Thus, we have the rather klunky setTimeout() call.
2102 window.addEventListener("sizemodechange", m_tb_resize_handler, false);
2104 let progress = Cc["@mozilla.org/docloaderservice;1"]
2105 .getService(Ci.nsIWebProgress);
2106 progress.removeProgressListener(this);
2110 onProgressChange: function(aProgress, aRequest, curSelfProgress,
2111 maxSelfProgress, curTotalProgress,
2112 maxTotalProgress) {},
2113 onStatusChange: function(aProgress, aRequest, stat, message) {},
2114 onSecurityChange: function() {}
2117 // aURI should be an http or https nsIURI object.
2118 function torbutton_get_current_accept_language_value(aURI)
2121 let ioService = Cc["@mozilla.org/network/io-service;1"]
2122 .getService(Ci.nsIIOService);
2123 let channel = ioService.newChannelFromURI(aURI);
2124 let httpChannel = channel.QueryInterface(Ci.nsIHttpChannel);
2125 return httpChannel.getRequestHeader("Accept-Language");
2131 // Take URL strings the user has specified for a homepage
2132 // and normalize it so it looks like a real URL.
2133 function torbutton_normalize_homepage_url_string(aURLString)
2135 if (!aURLString) return null;
2136 if (typeof aURLString !== "string") return null;
2139 url = new URL(aURLString);
2142 url = new URL("http://" + aURLString);
2150 function torbutton_is_homepage_url(aURI)
2156 let choice = m_tb_prefs.getIntPref("browser.startup.page");
2157 if ((1 == choice) || (3 == choice)) try {
2158 // A homepage may be used at startup. Get the values and check against
2160 homePageURLs = m_tb_prefs.getComplexValue("browser.startup.homepage",
2161 Ci.nsIPrefLocalizedString).data;
2167 let urls = homePageURLs.split('|')
2168 .map(torbutton_normalize_homepage_url_string);
2169 return (urls.indexOf(aURI.spec) >= 0);
2172 // Makes sure the item in the Help Menu and the link in about:tor
2173 // for the Tor Browser User Manual are only visible when
2174 // show_torbrowser_manual() returns true.
2175 function torbutton_init_user_manual_links() {
2176 let menuitem = document.getElementById("torBrowserUserManual");
2177 bindPrefAndInit("intl.locale.requested", val => {
2178 menuitem.hidden = !show_torbrowser_manual();
2179 torbutton_abouttor_message_handler.updateAllOpenPages();