Bug 29035: Post-YE campaign clean-up 2018
[torbutton.git] / src / chrome / content / torbutton.js
blobf99be5b0b4820c6487ead01d01ac45b894c9b4cf
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;
23 // status
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;
34 var m_tb_tbb = false;
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 =
48     register: function()
49     {
50         m_tb_prefs.addObserver("extensions.torbutton", this, false);
51     },
53     unregister: function()
54     {
55         m_tb_prefs.removeObserver("extensions.torbutton", this);
56     },
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)
62     {
63         if (topic != "nsPref:changed") return;
64         switch (data) {
65             case k_tb_browser_update_needed_pref:
66                 torbutton_notify_if_update_needed();
67                 break;
68         }
69     }
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 =
77     register: function()
78     {
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);
92     },
94     unregister: function()
95     {
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");
107     },
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)
113     {
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
127               */ 
128              if (!m_tb_confirming_plugins) {
129                m_tb_confirming_plugins = true;
130                torbutton_confirm_plugins();
131                m_tb_confirming_plugins = false;
132              } else {
133                torbutton_log(3, "Skipping notification for mime type: "+subject);
134              }
135           }
136           return;
137         }
139         if (topic != "nsPref:changed") return;
141         switch (data) {
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);
149                 break;
151             case "plugin.disable":
152                 torbutton_toggle_plugins(
153                         m_tb_prefs.getBoolPref("plugin.disable"));
154                 break;
155             case "browser.privatebrowsing.autostart":
156                 torbutton_update_disk_prefs();
157                 break;
158             case "extensions.torbutton.use_nontor_proxy":
159                 torbutton_use_nontor_proxy();
160                 break;
161             case "privacy.resistFingerprinting":
162                 torbutton_update_fingerprinting_prefs();
163                 break;
164             case "privacy.firstparty.isolate":
165                 torbutton_update_isolation_prefs();
166                 break;
167         }
168     }
171 var torbutton_tor_check_observer = {
172     register: function()
173     {
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);
177     },
179     unregister: function()
180     {
181         if (this._obsSvc)
182           this._obsSvc.removeObserver(this, k_tb_tor_check_failed_topic);
183     },
185     observe: function(subject, topic, data)
186     {
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
195         // window, open one.
196         var wm = Cc["@mozilla.org/appshell/window-mediator;1"]
197               .getService(Components.interfaces.nsIWindowMediator);
198         var win = wm.getMostRecentWindow("navigator:browser");
199         if (win == window) {
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");
205           }
207           if (!foundTab)
208             gBrowser.selectedTab = gBrowser.addTab("about:tor");
209         }
210       }
211     }
214 function torbutton_init_toolbutton()
216     try {
217       torbutton_log(3, "Initializing the Torbutton button.");
218       torbutton_update_toolbutton();
219     } catch(e) {
220       torbutton_log(4, "Error Initializing Torbutton button: "+e);
221     }
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) {
237         return;
238     }
239     m_tb_wasinited = true;
241     // Determine if we are running inside Tor Browser.
242     var cur_version;
243     try {
244       cur_version = m_tb_prefs.getCharPref("torbrowser.version");
245       m_tb_tbb = true;
246       torbutton_log(3, "This is a Tor Browser");
247     } catch(e) {
248       torbutton_log(3, "This is not a Tor Browser: "+e);
249     }
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.
254     var last_version;
255     try {
256       last_version = m_tb_prefs.getCharPref(k_tb_last_browser_version_pref);
257     } catch (e) {}
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);
262       }
264       if (cur_version)
265         m_tb_prefs.setCharPref(k_tb_last_browser_version_pref, cur_version);
266     }
268     let tlps;
269     try {
270         tlps = Cc["@torproject.org/torlauncher-protocol-service;1"]
271                  .getService(Ci.nsISupports).wrappedJSObject;
272     } catch(e) {}
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");
282         try {
283             if ("" != cookie_path) {
284                 m_tb_control_pass = torbutton_read_authentication_cookie(cookie_path);
285             }
286         } catch(e) {
287             torbutton_log(4, 'unable to read authentication cookie');
288         }
289     } else try {
290         // Try to get password from Tor Launcher.
291         m_tb_control_pass = tlps.TorGetPassword(false);
292     } catch(e) {}
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.
297     try {
298         m_tb_control_ipc_file = tlps.TorGetControlIPCFile();
299     } catch(e) {}
301     if (m_tb_control_ipc_file) {
302         m_tb_control_desc = m_tb_control_ipc_file.path;
303     } else {
304         if (environ.exists("TOR_CONTROL_PORT")) {
305             m_tb_control_port = environ.get("TOR_CONTROL_PORT");
306         } else {
307             try {
308                 const kTLControlPortPref = "extensions.torlauncher.control_port";
309                 m_tb_control_port = m_tb_prefs.getIntPref(kTLControlPortPref);
310             } catch(e) {
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
313               // port value here.
314             }
315         }
317         if (m_tb_control_port) {
318           m_tb_control_desc = "" + m_tb_control_port;
319         }
321         if (environ.exists("TOR_CONTROL_HOST")) {
322             m_tb_control_host = environ.get("TOR_CONTROL_HOST");
323         } else {
324             try {
325                 const kTLControlHostPref = "extensions.torlauncher.control_host";
326                 m_tb_control_host = m_tb_prefs.getCharPref(kTLControlHostPref);
327             } catch(e) {
328               m_tb_control_host = "127.0.0.1";
329             }
330         }
331     }
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");
344       try {
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();
350       } catch(e) {
351       } finally {
352         m_tb_prefs.setBoolPref("dom.quotaManager.testing", orig_quota_test);
353       }
354     }
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");
367     //if (contextMenu)
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');
375       try {
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.
379         try {
380           m_tb_prefs.clearUserPref("browser.uiCustomization.state");
381         } catch(e) {}
382         CustomizableUI.reset();
383         CustomizableUI.undoReset();
384         torbutton_log(3, 'Button added');
385         m_tb_prefs.setBoolPref("extensions.torbutton.inserted_button", true);
386       } catch(e) {
387         torbutton_log(4, 'Failed to add Torbutton to toolbar: '+e);
388       }
389     }
391     torbutton_update_toolbutton();
392     torbutton_notify_if_update_needed();
394     try {
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");
398     } catch(e) {
399         torbutton_log(4, "Error creating the tor circuit display " + e);
400     }
402     try {
403         torbutton_init_user_manual_links();
404     } catch(e) {
405         torbutton_log(4, "Error loading the user manual " + e);
406     }
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",
421                                                         this.chromeData);
422         break;
423     }
424   },
426   // Send privileged data to all of the about:tor content scripts.
427   updateAllOpenPages: function() {
428     window.messageManager.broadcastAsyncMessage("AboutTor:ChromeData",
429                                                 this.chromeData);
430   },
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
436   // not working.
437   get chromeData() {
438     return {
439       mobile: torbutton_is_mobile(),
440       torOn: torbutton_tor_check_ok()
441     };
442   }
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++) {
450       if (!P[i].disabled)
451         any_plugins_enabled = true;
452   }
454   if (!any_plugins_enabled) {
455     torbutton_log(3, "False positive on plugin notification. Ignoring");
456     return;
457   }
458   
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;
466       
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.
481   if (no_plugins != 
482       m_tb_prefs.getBoolPref("plugin.disable"))
483     m_tb_prefs.setBoolPref("plugin.disable", no_plugins);
484   else
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();
505       }
506     }
507   }
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");
542     } else {
543         torbutton_log(3, 'get_toolbutton(): did not find torbutton-button');
544     }
546     return o_toolbutton;
549 function torbutton_update_is_needed() {
550     var updateNeeded = false;
551     try {
552         updateNeeded = m_tb_prefs.getBoolPref(k_tb_browser_update_needed_pref);
553     } catch (e) {}
555     return updateNeeded;
558 function torbutton_notify_if_update_needed() {
559     function setOrClearAttribute(aElement, aAttrName, aValue)
560     {
561         if (!aElement || !aAttrName)
562             return;
564         if (aValue)
565             aElement.setAttribute(aAttrName, aValue);
566         else
567             aElement.removeAttribute(aAttrName);
568     }
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);
595     if (isPending)
596         prompter.showUpdateDownloaded(update, false);
597     else
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")) {
605     return;
606   }
608   // Suppress update check if done recently.
609   const kMinSecsBetweenChecks = 120 * 60; // 2.0 hours
610   var now = Date.now() / 1000;
611   var lastCheckTime;
612   try {
613     lastCheckTime = parseFloat(m_tb_prefs.getCharPref(k_tb_last_update_check_pref));
614     if (isNaN(lastCheckTime))
615       lastCheckTime = undefined;
616   } catch (e) {}
618   if (lastCheckTime && ((now - lastCheckTime) < kMinSecsBetweenChecks))
619     return;
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"));
625   try {
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!");
636             return -1;
637           }
638           try {
639             var version_list = JSON.parse(req.responseText);
640             var my_version = m_tb_prefs.getCharPref("torbrowser.version");
641             var platformSuffix;
642             var platform = Services.appinfo.OS;
643             switch (platform) {
644               case "WINNT":
645                 platformSuffix = "Windows";
646                 break;
647               case "Darwin":
648                 platformSuffix = "MacOS";
649                 break;
650               case "Linux":
651               case "Android":
652                 platformSuffix = platform;
653                 break;
654             }
655             if (platformSuffix)
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);
661               return;
662             }
663             torbutton_log(5, "Your Tor Browser is out of date.");
664             m_tb_prefs.setBoolPref(k_tb_browser_update_needed_pref, true);
665             return;
666           } catch(e) {
667             torbutton_log(5, "Version check failed! JSON parsing error: "+e);
668             return;
669           }
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.");
673           return -1;
674         }
675         torbutton_log(5, "Version check failed! Web server error: "+req.status);
676         return -1;
677       }  
678     };  
679     req.send(null);
680   } catch(e) {
681     if(e.result == 0x80004005) { // NS_ERROR_FAILURE
682       torbutton_log(5, "Version check failed! Is tor running?");
683       return -1;
684     }
685     torbutton_log(5, "Version check failed! Tor internal error: "+e);
686     return -1;
687   }
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) {
708   var str = "";
709   var bytes;
710   while((bytes = input.readBytes(1)) != "\n") {
711     if (bytes != '\r')
712       str += bytes;
713   }
714   return str;
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();
730   fileStream.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)
738                    }).join('');
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);
759   try {
760     let sts = Cc["@mozilla.org/network/socket-transport-service;1"]
761         .getService(Ci.nsISocketTransportService);
762     let socket;
763     if (m_tb_control_ipc_file) {
764       socket = sts.createUnixDomainTransport(m_tb_control_ipc_file);
765     } else {
766       socket = sts.createTransport(null, 0, m_tb_control_host,
767                                    m_tb_control_port, null);
768     }
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);
789       return null;
790     }
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);
796       return null;
797     }
799     // Closing these streams prevents a shutdown hang on Mac OS. See bug 10201.
800     inputStream.close();
801     outputStream.close();
802     socket.close(Cr.NS_OK);
803     return bytes.substr(4);
804   } catch(e) {
805     torbutton_log(4, "Exception on control port "+e);
806     return null;
807   }
810 // Bug 1506 P4: Needed for New IP Address
811 function torbutton_new_circuit() {
812   let firstPartyDomain = gBrowser.contentPrincipal.originAttributes
813                                  .firstPartyDomain;
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() {
825   try {
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");
836     if (shouldConfirm) {
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);
852       if (confirmed) {
853         torbutton_do_new_identity();
854       } else {
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;
860       }
861     } else {
862         torbutton_do_new_identity();
863     }
864   } catch(e) {
865     // If something went wrong make sure we have the New Identity button
866     // enabled (again).
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);
874   }
877 /* The "New Identity" implementation does the following:
878  *   1. Disables Javascript and plugins on all tabs
879  *   2. Clears state:
880  *      a. OCSP
881  *      b. Cache + image cache
882  *      c. Site-specific zoom
883  *      d. Cookies+DOM Storage+safe browsing key
884  *      e. google wifi geolocation token
885  *      f. http auth
886  *      g. SSL Session IDs
887  *      h. last open location url
888  *      i. clear content prefs
889  *      j. permissions
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.
897  */
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"));
909   try {
910       if(m_tb_prefs.prefHasUserValue("geo.wifi.access_token")) {
911           m_tb_prefs.clearUserPref("geo.wifi.access_token");
912       }
913   } catch(e) {
914       torbutton_log(3, "Exception on wifi token clear: "+e);
915   }
917   try {
918       if(m_tb_prefs.prefHasUserValue("general.open_location.last_url")) {
919           m_tb_prefs.clearUserPref("general.open_location.last_url");
920       }
921   } catch(e) {
922       torbutton_log(3, "Exception on clearing last opened location: "+e);
923   }
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.
931   try {
932     var searchBar = window.document.getElementById("searchbar");
933     if (searchBar)
934         searchBar.textbox.reset();
935   } catch(e) {
936     torbutton_log(5, "New Identity: Exception on clearing search box: "+e);
937   }
939   try {
940     if (gFindBarInitialized) {
941         var findbox = gFindBar.getElement("findbar-textbox");
942         findbox.reset();
943         gFindBar.close();
944     }
945   } catch(e) {
946     torbutton_log(5, "New Identity: Exception on clearing find bar: "+e);
947   }
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);
957       auth.clearAll();
958   }
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
964   // cache.
965   let sdr = Components.classes["@mozilla.org/security/sdr;1"].
966                        getService(Components.interfaces.nsISecretDecoderRing);
967   sdr.logoutAndTeardown();
969   // This clears the OCSP cache.
970   //
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?
982   try {
983       Services.perms.removeAll();
984   } catch(e) {
985       // Actually, this catch does not appear to be needed. Leaving it in for
986       // safety though.
987       torbutton_log(3, "Can't clear permissions: Not Tor Browser: "+e);
988   }
990    // Clear site security settings
991    let sss = Cc["@mozilla.org/ssservice;1"].
992      getService(Ci.nsISiteSecurityService);
993    sss.clearAll();
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");
1005   try {
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.
1017       try {
1018         appCacheStorage.asyncEvictStorage(null);
1019       } catch (err) {
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') {
1023              throw err;
1024          }
1025       }
1026     }
1027   } catch(e) {
1028       torbutton_log(5, "Exception on cache clearing: "+e);
1029       window.alert("Torbutton: Unexpected error during offline cache clearing: "+e);
1030   }
1032   torbutton_log(3, "New Identity: Clearing Disk and Memory Caches");
1034   try {
1035       Services.cache2.clear();
1036   } catch(e) {
1037       torbutton_log(5, "Exception on cache clearing: "+e);
1038       window.alert("Torbutton: Unexpected error during cache clearing: "+e);
1039   }
1041   torbutton_log(3, "New Identity: Clearing storage");
1043   let orig_quota_test = m_tb_prefs.getBoolPref("dom.quotaManager.testing");
1044   try {
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();
1050   } catch(e) {
1051       torbutton_log(5, "Exception on storage clearing: "+e);
1052   } finally {
1053       m_tb_prefs.setBoolPref("dom.quotaManager.testing", orig_quota_test);
1054   }
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)
1061                     .wrappedJSObject;
1062     // This emits "cookie-changed", "cleared", which kills DOM storage
1063     // and the safe browsing API key
1064     selector.clearUnprotectedCookies("tor");
1065   } else {
1066     torbutton_clear_cookies();
1067   }
1069   torbutton_log(3, "New Identity: Closing open connections");
1071   // Clear keep-alive
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();
1089   }
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);
1100   pm.removeAll();
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);
1116   } else {
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);
1121     }
1122   }
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();
1157     }
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();
1164     }
1166     torbutton_log(3, "Completed New Identity GC pass");
1167   });
1169   // Close the current window for added safety
1170   window.close();
1173 function torbutton_clear_image_caches()
1175   try {
1176     let imgCache;
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
1182     } else {
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"))
1200           continue;
1202         let tabbrowser = win.getBrowser();
1203         if (!tabbrowser)
1204           continue;
1206         var tabCount = tabbrowser.browsers.length;
1207         for (var i = 0; i < tabCount; i++) {
1208           let doc = tabbrowser.browsers[i].contentDocument;
1209           if (doc) {
1210             imgCache = imgTools.getImgCacheForDocument(doc);
1211             imgCache.clearCache(false); // evict all but chrome cache
1212             didClearPBCache = true;
1213             break;
1214           }
1215         }
1216       }
1217     }
1218   } catch(e) {
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);
1224   }
1227 /* Called when we switch the use_nontor_proxy pref in either direction.
1229  * Enables/disables domain isolation and then does new identity
1230  */
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();
1239   } else {
1240     domainIsolator.enableIsolation();
1241   }
1243   // Always reset our identity if the proxy has changed from tor
1244   // to non-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;
1269     else {
1270       // The check failed.  Update toolbar icon and tooltip.
1271       checkSvc.statusOfTorCheck = checkSvc.kCheckFailed;
1272       torbutton_update_toolbutton();
1273     }
1274   }
1275   else {
1276     // A local check is not possible, so perform a remote check.
1277     torbutton_initiate_remote_tor_check();
1278   }
1281 function torbutton_local_tor_check()
1283   let didLogError = false;
1285   let proxyType = m_tb_prefs.getIntPref("network.proxy.type");
1286   if (0 == proxyType)
1287     return false;
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");
1293   if (!resp)
1294     return false;
1296   function logUnexpectedResponse()
1297   {
1298     if (!didLogError) {
1299       didLogError = true;
1300       torbutton_log(5, "Local Tor check: unexpected GETINFO response: " + resp);
1301     }
1302   }
1304   function removeBrackets(aStr)
1305   {
1306     // Remove enclosing square brackets if present.
1307     if (aStr.startsWith('[') && aStr.endsWith(']'))
1308       return aStr.substr(1, aStr.length - 2);
1310     return aStr;
1311   }
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();
1317     return false;
1318   }
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");
1327   let socksIPCPath;
1328   if (socksAddr && socksAddr.startsWith("file:")) {
1329     // Convert the file URL to a file path.
1330     try {
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;
1336     } catch (e) {
1337       torbutton_log(5, "Local Tor check: IPC file error: " + e);
1338       return false;
1339     }
1340   } else {
1341     socksAddr = removeBrackets(socksAddr);
1342   }
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.
1347   let addrArray = [];
1348   resp.replace(/((\S*?"(.*?)")+\S*|\S+)/g, function (a, captured) {
1349     addrArray.push(captured);
1350   });
1352   let foundSocksListener = false;
1353   for (let i = 0; !foundSocksListener && (i < addrArray.length); ++i) {
1354     let addr;
1355     try { addr = unescapeTorString(addrArray[i]); } catch (e) {}
1356     if (!addr)
1357       continue;
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:")) {
1365       if (!socksIPCPath)
1366         continue;
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(':');
1378       if (idx < 0) {
1379         logUnexpectedResponse();
1380       } else {
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();
1385         } else {
1386           torbutton_log(2, "Tor socks listener: " + torSocksAddr + ':'
1387                            + torSocksPort);
1388           foundSocksListener = ((socksAddr === torSocksAddr) &&
1389                                 (socksPort === torSocksPort));
1390         }
1391       }
1392     }
1393   }
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);
1404   try {
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);
1424           }
1425       };
1427       torbutton_log(3, "Sending async Tor remote check");
1428       req.send(null);
1429   } catch(e) {
1430     if (e.result == 0x80004005) // NS_ERROR_FAILURE
1431       torbutton_log(5, "Tor check failed! Is tor running?");
1432     else
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);
1437   }
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) {
1452   if (m_tb_tbb) {
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;
1465         }
1466     }
1467   }
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);
1486     if (mode) {
1487         m_tb_prefs.setIntPref("network.cookie.lifetimePolicy", 2);
1488         m_tb_prefs.setIntPref("browser.download.manager.retention", 1);
1489     } else {
1490         m_tb_prefs.setIntPref("network.cookie.lifetimePolicy", 0);
1491         m_tb_prefs.setIntPref("browser.download.manager.retention", 2);
1492     }
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");
1516     if (isolate) {
1517       m_tb_prefs.setIntPref("network.cookie.cookieBehavior", 1);
1518     } else {
1519       m_tb_prefs.setIntPref("network.cookie.cookieBehavior", 0);
1520     }
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");
1533     return;
1534   }
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();
1545     if (!browser) {
1546       torbutton_log(5, "No browser for possible closed window");
1547       continue;
1548     }
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]);
1555       if (!tab) {
1556         torbutton_log(5, "No tab for browser");
1557       } else {
1558         tabsToRemove.push(tab);
1559       }
1560     }
1562     if (win == window) {
1563       browser.addTab("about:blank");
1564     } else {
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);
1568     }
1570     // Close each tab except the new blank one that we created.
1571     tabsToRemove.forEach(aTab => browser.removeTab(aTab));
1572   }
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;
1592   else
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;
1601   } else {
1602     document.getElementById("torbutton-checkForUpdateSeparator").hidden = false;
1603     document.getElementById("torbutton-checkForUpdate").hidden = false;
1604   }
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;
1619   }
1621   if (!m_tb_tbb && m_tb_prefs.getBoolPref("extensions.torbutton.prompt_torbrowser")) {
1622       torbutton_inform_about_tbb();
1623   }
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
1642 // not in use.
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);
1647    
1648     cm.removeAll();
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.
1659     try {
1660         if (!browser.contentWindow)
1661             torbutton_log(3, "No content window to disable JS events.");
1662         else
1663             eventSuppressor = browser.contentWindow.
1664                 QueryInterface(Components.interfaces.nsIInterfaceRequestor).
1665                        getInterface(Ci.nsIDOMWindowUtils);
1666     } catch(e) {
1667         torbutton_log(4, "Failed to disable JS events: "+e)
1668     }
1669    
1670     if (browser.docShell)
1671       browser.docShell.allowJavascript = false;
1673     try {
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;
1679     } catch(e) {
1680         torbutton_log(4, "Failed to reset window.name: "+e)
1681     }
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();
1691     if(!browser) {
1692       torbutton_log(5, "No browser for plugin window...");
1693       return;
1694     }
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) {
1701             try {
1702                 if (b.currentURI) 
1703                     torbutton_log(5, "DocShell is null for: "+b.currentURI.spec);
1704                 else 
1705                     torbutton_log(5, "DocShell is null for unknown URL");
1706             } catch(e) {
1707                 torbutton_log(5, "DocShell is null for unparsable URL: "+e);
1708             }
1709         }
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?
1716             try {
1717               if (b.docShell && b.webNavigation)
1718                 b.webNavigation.stop(b.webNavigation.STOP_ALL);
1719             } catch(e) {
1720               torbutton_log(4, "DocShell error: "+e);
1721             }
1722         }
1723     }
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);
1738     }
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
1754             // URLs.
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);
1758         }
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");
1764         }
1766         m_tb_prefs.setIntPref('extensions.torbutton.pref_fixup_version', 1);
1767     }
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();
1795         // For charsets
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.
1801         if (!m_tb_tbb) {
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);
1805         }
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);
1813         }
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);
1819     }
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");
1842         return false;
1843     }
1844     if ("fullScreen" in wind && wind.fullScreen) {
1845         torbutton_log(2, "Window is fullScreen");
1846         return false;
1847     }
1848     if(wind.outerHeight == wind.screen.availHeight 
1849             && wind.outerWidth == wind.screen.availWidth) {
1850         torbutton_log(3, "Window is ratpoisoned/evilwm'ed");
1851         return false;
1852     }
1853         
1854     torbutton_log(2, "Window is normal");
1855     return true;
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) {
1869           settingsTab = tab;
1870           return true;
1871       }
1872       return false;
1873   });
1875   if (settingsTab === null) {
1876       // Open up the settings panel in a new tab.
1877       tabBrowser.addTab(SECURITY_PREFERENCES_URI, {
1878           'selected': true,
1879           'parentId': tabBrowser.selectedTab.id
1880       });
1881   } else {
1882       // Activate an existing settings panel tab.
1883       tabBrowser.selectTab(settingsTab);
1884   }
1887 function setupPreferencesForMobile() {
1888   if (!torbutton_is_mobile()) {
1889     return;
1890   }
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)
1900   });
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
1905 // details
1906 function torbutton_new_window(event)
1908     torbutton_log(3, "New window");
1909     var browser = window.gBrowser;
1911     if(!browser) {
1912       torbutton_log(5, "No browser for new window.");
1913       return;
1914     }
1916     m_tb_window_height = window.outerHeight;
1917     m_tb_window_width = window.outerWidth;
1919     if (!m_tb_wasinited) {
1920         torbutton_init();
1921     }
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);
1934     }
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,
1955         false);
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;
1978                 break;
1979             }
1980         }
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;
1990         }
1991     }
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)
2013   {
2014    if (aIID.equals(Ci.nsIWebProgressListener) ||
2015        aIID.equals(Ci.nsISupportsWeakReference) ||
2016        aIID.equals(Ci.nsISupports))
2017      return this;
2018    throw Cr.NS_NOINTERFACE;
2019   },
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) {
2029           if (m_tb_prefs.
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))
2036               return;
2038             // Rate-limit showing our notification if needed.
2039             if (m_tb_resize_date === null) {
2040               m_tb_resize_date = Date.now();
2041             } else {
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) {
2046                 return;
2047               }
2048               // Resizing but we need to reset |m_tb_resize_date| now.
2049               m_tb_resize_date = Date.now();
2050             }
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);
2056             let bundle = sbSvc.
2057               createBundle("chrome://global/locale/commonDialogs.properties");
2058             let button_label = bundle.GetStringFromName("OK");
2060             let buttons = [{
2061               label: button_label,
2062               accessKey: 'O',
2063               popup: null,
2064               callback:
2065                 function() {
2066                   m_tb_prefs.setIntPref("extensions.torbutton.maximize_warnings_remaining",
2067                   m_tb_prefs.getIntPref("extensions.torbutton.maximize_warnings_remaining") - 1);
2068                 }
2069             }];
2071             let priority = box.PRIORITY_WARNING_LOW;
2072             let message =
2073               torbutton_get_property_string("torbutton.maximize_warning");
2075             box.appendNotification(message, kNotificationName, null,
2076                                    priority, buttons);
2077             return;
2078           }
2079         }
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
2088       // #14229 lands.
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);
2107     }
2108   }, // onStateChange
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)
2120   try {
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");
2126   } catch (e) {}
2128   return null;
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;
2137   let url;
2138   try {
2139     url = new URL(aURLString);
2140   } catch (e) {
2141     try {
2142       url = new URL("http://" + aURLString);
2143     } catch (e) {
2144       return null;
2145     }
2146   }
2147   return url.href;
2150 function torbutton_is_homepage_url(aURI)
2152   if (!aURI)
2153     return false;
2155   let homePageURLs;
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
2159      // aURI.spec.
2160      homePageURLs = m_tb_prefs.getComplexValue("browser.startup.homepage",
2161                                                Ci.nsIPrefLocalizedString).data;
2162   } catch (e) {}
2164   if (!homePageURLs)
2165     return false;
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();
2180   });
2183 //vim:set ts=4