[tor-commits] [torbutton/master] Bug 20347: Remove "Custom" checkbox; show "Restore defaults" button

gk at torproject.org gk at torproject.org
Mon Nov 7 09:36:54 UTC 2016


commit e59f6df84dcfde500f41eefb608dd878a7fc9d6b
Author: Arthur Edelstein <arthuredelstein at gmail.com>
Date:   Mon Oct 17 17:25:59 2016 -0700

    Bug 20347: Remove "Custom" checkbox; show "Restore defaults" button
    
    This patch moves the security slider code from torbutton.js
    to its own file (security-prefs.js) and refactors it.
---
 src/chrome/content/preferences.js  | 149 ++++++------------
 src/chrome/content/preferences.xul |  53 ++++---
 src/chrome/content/torbutton.js    | 309 ++++---------------------------------
 src/chrome/locale/en/torbutton.dtd |   5 +-
 src/modules/security-prefs.js      | 130 ++++++++++++++++
 src/modules/utils.js               |  21 ++-
 6 files changed, 256 insertions(+), 411 deletions(-)

diff --git a/src/chrome/content/preferences.js b/src/chrome/content/preferences.js
index 1d85cde..7856ef1 100644
--- a/src/chrome/content/preferences.js
+++ b/src/chrome/content/preferences.js
@@ -1,105 +1,44 @@
-// Bug 1506 P1: Most of this code needs to go away. See also Bug 3100.
-
-// PREFERences dialog functions
-//   torbutton_prefs_init() -- on dialog load
-//   torbutton_prefs_save() -- on dialog save
-
-const Cc = Components.classes, Ci = Components.interfaces;
-
-
-function torbutton_prefs_init(doc) {
-    torbutton_log(2, "called prefs_init()");
-
-    var o_torprefs = torbutton_get_prefbranch('extensions.torbutton.');
-
-    let sec_slider = doc.getElementById('torbutton_sec_slider');
-    let sec_custom = doc.getElementById('torbutton_sec_custom');
-    let custom_values = o_torprefs.getBoolPref('security_custom');
-    sec_slider.value = o_torprefs.getIntPref('security_slider');
-    sec_custom.checked = custom_values;
-    sec_custom.disabled = !custom_values;
-    torbutton_set_slider_text(doc, sec_custom.checked);
-    // If the custom checkbox is checked and the user is done with dragging
-    // uncheck the checkbox to allow setting the (newly) chosen security level.
-    sec_slider.dragStateChanged = function(isDragging) {
-        if (!isDragging && sec_custom.checked) {
-           sec_custom.checked = false;
-           sec_custom.disabled = true;
-        }
-    }
-    sec_slider.valueChanged = function(which, newValue, userChanged) {
-        torbutton_set_slider_text(doc, false);
-    }
-}
-
-function torbutton_prefs_save(doc) {
-    // Disable the Accept button once the user clicked on it as clicking on
-    // our active Accept button more than once can lead to all sort of weird
-    // behavior. See bug 11763 for an example.
-    doc.documentElement.getButton("accept").disabled = true;
-    torbutton_log(2, "called prefs_save()");
-    var o_torprefs = torbutton_get_prefbranch('extensions.torbutton.');
-
-    o_torprefs.setBoolPref('security_custom',
-                           doc.getElementById('torbutton_sec_custom').checked);
-    o_torprefs.setIntPref('security_slider',
-                          doc.getElementById('torbutton_sec_slider').value);
-
-    // If we have non-custom Security Slider settings update them now.
-    if (!o_torprefs.getBoolPref('security_custom')) {
-      let wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
-                         .getService(Components.interfaces.nsIWindowMediator);
-      let win = wm.getMostRecentWindow("navigator:browser");
-      win.torbutton_update_security_slider();
-    }
-}
-
-function torbutton_toggle_slider(doc, pos) {
-    doc.getElementById("torbutton_sec_slider").value = pos;
-    // Make sure the custom checkbox is unchecked as the user seems to want one
-    // of the defined security levels.
-    let sec_custom = doc.getElementById("torbutton_sec_custom");
-    if (sec_custom.checked) {
-        sec_custom.checked = false;
-    }
-    torbutton_set_slider_text(doc, false);
-}
-
-function torbutton_set_slider_text(doc, custom) {
-  let level = doc.getElementById("torbutton_sec_slider").value;
-  if (custom) {
-    level = 5;
-  }
-  switch (level) {
-    case (1):
-      doc.getElementById("desc_low").collapsed = true;
-      doc.getElementById("desc_medium_low").collapsed = true;
-      doc.getElementById("desc_medium_high").collapsed = true;
-      doc.getElementById("desc_high").collapsed = false;
-      break;
-    case (2):
-      doc.getElementById("desc_low").collapsed = true;
-      doc.getElementById("desc_medium_low").collapsed = true;
-      doc.getElementById("desc_medium_high").collapsed = false;
-      doc.getElementById("desc_high").collapsed = true;
-      break;
-   case (3):
-      doc.getElementById("desc_low").collapsed = true;
-      doc.getElementById("desc_medium_low").collapsed = false;
-      doc.getElementById("desc_medium_high").collapsed = true;
-      doc.getElementById("desc_high").collapsed = true;
-      break;
-    case (4):
-      doc.getElementById("desc_low").collapsed = false;
-      doc.getElementById("desc_medium_low").collapsed = true;
-      doc.getElementById("desc_medium_high").collapsed = true;
-      doc.getElementById("desc_high").collapsed = true;
-      break;
-    case (5):
-      doc.getElementById("desc_low").collapsed = true;
-      doc.getElementById("desc_medium_low").collapsed = true;
-      doc.getElementById("desc_medium_high").collapsed = true;
-      doc.getElementById("desc_high").collapsed = true;
-      break;
-  }
-}
+// # Security Settings User Interface
+
+// Utilities
+let { utils: Cu } = Components;
+let { getBoolPref, getIntPref, setBoolPref, setIntPref } =
+    Cu.import("resource://gre/modules/Services.jsm").Services.prefs;
+
+// Description elements have the follow names.
+const descNames =
+      [, "desc_high", "desc_medium_high", "desc_medium_low", "desc_low"];
+
+// A single `state` object that reflects the user settings in this UI.
+let state = { slider : 0, custom : false};
+
+// Set the desired slider value and update UI.
+function torbutton_set_slider(sliderPosition) {
+  state.slider = sliderPosition;
+  let slider = document.getElementById("torbutton_sec_slider");
+  slider.value = sliderPosition;
+  let descs = descNames.map(name => document.getElementById(name));
+  descs.forEach((desc, i) => desc.collapsed = sliderPosition !== i);
+};
+
+// Set the desired custom value and update UI.
+function torbutton_set_custom(customValue) {
+  state.custom = customValue;
+  let sliderSettings = document.getElementById("torbutton_slider_settings");
+  let customSettings = document.getElementById("torbutton_custom_settings");
+  sliderSettings.hidden = customValue;
+  customSettings.hidden = !customValue;
+};
+
+// Read prefs 'extensions.torbutton.security_slider' and
+// 'extensions.torbutton.security_custom', and initialize the UI.
+function torbutton_init_security_ui() {
+  torbutton_set_slider(getIntPref("extensions.torbutton.security_slider"));
+  torbutton_set_custom(getBoolPref("extensions.torbutton.security_custom"));
+};
+
+// Write the two prefs from the current settings.
+function torbutton_save_security_settings() {
+  setIntPref("extensions.torbutton.security_slider", state.slider);
+  setBoolPref("extensions.torbutton.security_custom", state.custom);
+};
diff --git a/src/chrome/content/preferences.xul b/src/chrome/content/preferences.xul
index 574932e..53abd7f 100644
--- a/src/chrome/content/preferences.xul
+++ b/src/chrome/content/preferences.xul
@@ -10,64 +10,59 @@
         title="&torbutton.prefs.security_settings;"
         buttons="accept,cancel"
         persist="screenX screenY width height"
-        onload="torbutton_prefs_init(document)"
+        onload="torbutton_init_security_ui()"
         align="stretch"
         pack="center"
-        maxheight="350"
+        maxheight="300"
+        minwidth="300"
         maxwidth="400"
-        ondialogaccept="torbutton_prefs_save(document)" >
+        ondialogaccept="torbutton_save_security_settings()"
+        width="400" >
 
     <script type="application/x-javascript" src="torbutton_util.js"/>
     <script type="application/x-javascript" src="preferences.js"/>
     <vbox flex="1" align="stretch">
         <groupbox align="stretch" flex="1"> <!-- security settings container -->
           <caption label="&torbutton.prefs.sec_caption;"/>
-          <hbox flex="1" align="stretch">
+          <hbox id="torbutton_slider_settings" flex="1" align="stretch" hidden="false">
             <vbox>
               <hbox height="200">
                 <vbox>
                   <scale id="torbutton_sec_slider" flex="1" min="1" max="4"
-                         movetoclick="true" orient="vertical"/>
+                         movetoclick="true" orient="vertical"
+                         onchange="torbutton_set_slider(this.value)"/>
                 </vbox>
                 <vbox>
                   <hbox flex="1" align="start">
                     <description id="torbutton_sec_high"
-                                 tooltip="high_preview"
-                                 onclick="torbutton_toggle_slider(document, 1);">
+                                 onclick="torbutton_set_slider(1);"
+                                 tooltip="high_preview">
                       &torbutton.prefs.sec_high;
                     </description>
                   </hbox>
                   <hbox flex="1" align="center">
                     <description id="torbutton_sec_med_high"
-                                 tooltip="mh_preview"
-                                 onclick="torbutton_toggle_slider(document, 2);">
+                                 onclick="torbutton_set_slider(2);"
+                                 tooltip="mh_preview">
                       &torbutton.prefs.sec_med_high;
                     </description>
                   </hbox>
                   <hbox flex="1" align="center">
                     <description id="torbutton_sec_med_low"
-                                 tooltip="ml_preview"
-                                 onclick="torbutton_toggle_slider(document, 3);">
+                                 onclick="torbutton_set_slider(3);"
+                                 tooltip="ml_preview">
                       &torbutton.prefs.sec_med_low;
                     </description>
                   </hbox>
                   <hbox flex="1" align="end">
                     <description id="torbutton_sec_low"
-                                 tooltip="low_preview"
-                                 onclick="torbutton_toggle_slider(document, 4);">
+                                 onclick="torbutton_set_slider(4);"
+                                 tooltip="low_preview">
                       &torbutton.prefs.sec_low;
                     </description>
                   </hbox>
                 </vbox>
               </hbox>
-              <hbox>
-                <!-- We are using |oncommand| instead of |onclick| as the former does
-                     not fire if the checkbox is disabled and it does fire after the
-                     checkbox adapted its state. -->
-                <checkbox id="torbutton_sec_custom" flex="1"
-                          oncommand="torbutton_set_slider_text(document, event.target.checked);"
-                          label="&torbutton.prefs.sec_custom;"/>
-              </hbox>
             </vbox>
             <!-- A width of 400 is already too much for OS X it seems. The above
                  spacer tag would basically be useless and the layout ugly. -->
@@ -161,6 +156,20 @@
               </vbox>
             </vbox>
           </hbox>
+          <vbox id="torbutton_custom_settings"
+                hidden="true"
+                width="300"
+                height="200"
+                style="overflow:auto;">
+            <description>
+              &torbutton.prefs.custom_warning;
+            </description>
+            <hbox>
+              <button id="torbutton_restore_defaults_button"
+                      oncommand="torbutton_set_custom(false);"
+                      label="&torbutton.prefs.restore_defaults;"/>
+            </hbox>
+          </vbox>
         </groupbox>
       </vbox>
 
@@ -200,4 +209,4 @@
      <html:br></html:br>
      <html:div>&torbutton.prefs.sec_low_usable_desc;</html:div>
    </tooltip>
-</dialog>
+ </dialog>
diff --git a/src/chrome/content/torbutton.js b/src/chrome/content/torbutton.js
index 9a8b9e2..60f12d6 100644
--- a/src/chrome/content/torbutton.js
+++ b/src/chrome/content/torbutton.js
@@ -11,6 +11,7 @@ let { LoadContextInfo } = Cu.import('resource://gre/modules/LoadContextInfo.jsm'
 let { Services } = Cu.import("resource://gre/modules/Services.jsm");
 let { showDialog } = Cu.import("resource://torbutton/modules/utils.js");
 let { unescapeTorString } = Cu.import("resource://torbutton/modules/utils.js");
+let SecurityPrefs = Cu.import("resource://torbutton/modules/security-prefs.js");
 
 const k_tb_last_browser_version_pref = "extensions.torbutton.lastBrowserVersion";
 const k_tb_browser_update_needed_pref = "extensions.torbutton.updateNeeded";
@@ -45,8 +46,6 @@ var m_tb_orig_BrowserOnAboutPageLoad = null;
 var m_tb_domWindowUtils = window.QueryInterface(Ci.nsIInterfaceRequestor).
                           getInterface(Ci.nsIDOMWindowUtils);
 
-var m_tb_sliderUpdate = false;
-
 // Bug 1506 P1: This object is only for updating the UI for toggling and style
 var torbutton_window_pref_observer =
 {
@@ -86,11 +85,7 @@ var torbutton_unique_pref_observer =
         m_tb_prefs.addObserver("network.cookie", this, false);
         m_tb_prefs.addObserver("browser.privatebrowsing.autostart", this, false);
         m_tb_prefs.addObserver("javascript", this, false);
-        m_tb_prefs.addObserver("gfx", this, false);
         m_tb_prefs.addObserver("noscript", this, false);
-        m_tb_prefs.addObserver("media", this, false);
-        m_tb_prefs.addObserver("mathml", this, false);
-        m_tb_prefs.addObserver("svg", this, false);
         m_tb_prefs.addObserver("plugin.disable", this, false);
         m_tb_prefs.addObserver("privacy.thirdparty.isolate", this, false);
         m_tb_prefs.addObserver("privacy.resistFingerprinting", this, false);
@@ -107,11 +102,10 @@ var torbutton_unique_pref_observer =
         m_tb_prefs.removeObserver("network.cookie", this);
         m_tb_prefs.removeObserver("browser.privatebrowsing.autostart", this);
         m_tb_prefs.removeObserver("javascript", this);
-        m_tb_prefs.removeObserver("gfx", this);
         m_tb_prefs.removeObserver("noscript", this);
-        m_tb_prefs.removeObserver("media", this);
-        m_tb_prefs.removeObserver("mathml", this);
-        m_tb_prefs.removeObserver("svg", this);
+        m_tb_prefs.removeObserver("plugin.disable", this);
+        m_tb_prefs.removeObserver("privacy.thirdparty.isolate", this);
+        m_tb_prefs.removeObserver("privacy.resistFingerprinting", this);
 
         var observerService = Cc["@mozilla.org/observer-service;1"].
             getService(Ci.nsIObserverService);
@@ -149,7 +143,9 @@ var torbutton_unique_pref_observer =
         }
  
         if (topic != "nsPref:changed") return;
-
+        if (data.startsWith("noscript.")) {
+          torbutton_update_noscript_button();
+        }
         switch (data) {
             case "network.cookie.cookieBehavior":
                 var val = m_tb_prefs.getIntPref("network.cookie.cookieBehavior");
@@ -180,42 +176,6 @@ var torbutton_unique_pref_observer =
             case "extensions.torbutton.hide_sync_ui":
                 torbutton_update_sync_ui();
                 break;
-            case "gfx.font_rendering.opentype_svg.enabled":
-            case "javascript.options.ion.content":
-            case "javascript.options.typeinference":
-            case "noscript.forbidMedia":
-            case "media.webaudio.enabled":
-            case "mathml.disabled":
-            case "javascript.options.baselinejit.content":
-            case "noscript.forbidFonts":
-            case "noscript.globalHttpsWhitelist":
-            case "noscript.global":
-            case "svg.in-content.enabled":
-                // |m_tb_slider_update| is only set if the user updated a
-                // preference under control of the security slider via the
-                // slider on the Torbutton dialog. This in turn means we can
-                // skip the code dealing with setting/unsetting the custom mode
-                // in this case.
-                if (!m_tb_sliderUpdate) {
-                    // Do we already have custom settings?
-                    let customSlider = m_tb_prefs.
-                        getBoolPref("extensions.torbutton.security_custom");
-                    // A preference governed by the security slider got changed
-                    // but we are not in custom mode yet. Change that.
-                    if (!customSlider) {
-                        m_tb_prefs.
-                            setBoolPref("extensions.torbutton.security_custom",
-                            true);
-                    } else {
-                        // We are in custom mode. Check whether all prefs are
-                        // reset and reset the mode if so. Otherwise we remain
-                        // in custom mode.
-                        torbutton_log(4, "custom mode and we got: " + data);
-                        torbutton_security_slider_custom_check(m_tb_prefs.
-                            getIntPref("extensions.torbutton.security_slider"));
-                    }
-                }
-                break;
         }
     }
 }
@@ -269,7 +229,9 @@ function torbutton_init_toolbutton()
 // called once per browser window.. This might belong in a component.
 function torbutton_init() {
     torbutton_log(3, 'called init()');
-    
+
+    SecurityPrefs.initialize();
+
     if (m_tb_wasinited) {
         return;
     }
@@ -372,12 +334,6 @@ function torbutton_init() {
       torbutton_on_abouttor_load(aEvent.target);
     }, false, true);
 
-    // Set some important security prefs according to the chosen security level
-    // if there are no custom settings to respect.
-    if (!m_tb_prefs.getBoolPref("extensions.torbutton.security_custom")) {
-      torbutton_update_security_slider();
-    }
-
     // XXX: Get rid of the cached asmjs (or IndexedDB) files on disk in case we
     // don't allow things saved to disk. This is an ad-hoc fix to work around
     // #19417. Once this is properly solved we should remove this code again.
@@ -1805,228 +1761,6 @@ function torbutton_update_thirdparty_prefs() {
     m_tb_prefs.savePrefFile(null);
 }
 
-var torbutton_sec_ml_bool_prefs = {
-  "javascript.options.ion.content" : false,
-  "javascript.options.typeinference" : false,
-  "noscript.forbidMedia" : true,
-  "media.webaudio.enabled" : false,
-  "mathml.disabled" : true
-};
-
-var torbutton_sec_mh_bool_prefs = {
-  "javascript.options.baselinejit.content" : false,
-  "gfx.font_rendering.opentype_svg.enabled" : false,
-  "noscript.global" : false,
-  "noscript.globalHttpsWhitelist" : true
-};
-
-var torbutton_sec_h_bool_prefs = {
-  "noscript.forbidFonts" : true,
-  "noscript.global" : false,
-  "svg.in-content.enabled" : false
-};
-
-function torbutton_update_security_slider() {
-  // Avoid checking the custom settings checkbox.
-  m_tb_sliderUpdate = true;
-  let mode = m_tb_prefs.getIntPref("extensions.torbutton.security_slider");
-  let capValue = m_tb_prefs.getCharPref("capability.policy.maonoscript.sites");
-  switch (mode) {
-   case 1:
-      for (p in torbutton_sec_ml_bool_prefs) {
-        m_tb_prefs.setBoolPref(p, torbutton_sec_ml_bool_prefs[p])
-      }
-      for (p in torbutton_sec_mh_bool_prefs) {
-        m_tb_prefs.setBoolPref(p, torbutton_sec_mh_bool_prefs[p])
-        // noscript.globalHttpsWhitelist is special: We don't want it in this
-        // mode.
-        if (p === "noscript.globalHttpsWhitelist") {
-          m_tb_prefs.setBoolPref(p, !torbutton_sec_mh_bool_prefs[p])
-        }
-      }
-      for (p in torbutton_sec_h_bool_prefs) {
-        m_tb_prefs.setBoolPref(p, torbutton_sec_h_bool_prefs[p])
-      }
-      // Removing "https:" is needed due to a bug in older Noscript versions.
-      // We leave that in for a while as there may be users that were affected
-      // by this bug. Removing it immediately and having the auto-updater might
-      // leave users exposed to the problem.
-      if (capValue.indexOf(" https:") >= 0) {
-        m_tb_prefs.setCharPref("capability.policy.maonoscript.sites",
-          capValue.replace(" https:", ""));
-      }
-      break;
-    case 2:
-      for (p in torbutton_sec_ml_bool_prefs) {
-        m_tb_prefs.setBoolPref(p, torbutton_sec_ml_bool_prefs[p])
-      }
-      // Order matters here as both the high mode and the medium-high mode
-      // share some preferences/values. So, let's revert the high mode
-      // preferences first and set the medium-high mode ones afterwards.
-      for (p in torbutton_sec_h_bool_prefs) {
-        m_tb_prefs.setBoolPref(p, !torbutton_sec_h_bool_prefs[p])
-      }
-      for (p in torbutton_sec_mh_bool_prefs) {
-        m_tb_prefs.setBoolPref(p, torbutton_sec_mh_bool_prefs[p])
-      }
-      break;
-    case 3:
-      for (p in torbutton_sec_ml_bool_prefs) {
-        m_tb_prefs.setBoolPref(p, torbutton_sec_ml_bool_prefs[p])
-      }
-      for (p in torbutton_sec_mh_bool_prefs) {
-        m_tb_prefs.setBoolPref(p, !torbutton_sec_mh_bool_prefs[p])
-      }
-      for (p in torbutton_sec_h_bool_prefs) {
-        m_tb_prefs.setBoolPref(p, !torbutton_sec_h_bool_prefs[p])
-      }
-      // Removing "https:" is needed due to a bug in older Noscript versions.
-      // We leave that in for a while as there may be users that were affected
-      // by this bug. Removing it immediately and having the auto-updater might
-      // leave users exposed to the problem.
-      if (capValue.indexOf(" https:") >= 0) {
-        m_tb_prefs.setCharPref("capability.policy.maonoscript.sites",
-          capValue.replace(" https:", ""));
-      }
-      break;
-    case 4:
-      for (p in torbutton_sec_ml_bool_prefs) {
-        m_tb_prefs.setBoolPref(p, !torbutton_sec_ml_bool_prefs[p])
-      }
-      for (p in torbutton_sec_mh_bool_prefs) {
-        m_tb_prefs.setBoolPref(p, !torbutton_sec_mh_bool_prefs[p])
-      }
-      for (p in torbutton_sec_h_bool_prefs) {
-        m_tb_prefs.setBoolPref(p, !torbutton_sec_h_bool_prefs[p])
-      }
-      // Removing "https:" is needed due to a bug in older Noscript versions.
-      // We leave that in for a while as there may be users that were affected
-      // by this bug. Removing it immediately and having the auto-updater might
-      // leave users exposed to the problem.
-      if (capValue.indexOf(" https:") >= 0) {
-        m_tb_prefs.setCharPref("capability.policy.maonoscript.sites",
-          capValue.replace(" https:", ""));
-      }
-      break;
-  }
-  /* Update the NoScript button to reflect any changes */
-  try {
-      let wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
-                         .getService(Components.interfaces.nsIWindowMediator);
-      let browserEnumerator = wm.getEnumerator("navigator:browser");
-
-      // Update every window's NoScript status...
-      while (browserEnumerator.hasMoreElements()) {
-          let win = browserEnumerator.getNext();
-          win.noscriptOverlay._syncUINow();
-      }
-      torbutton_log(3, 'Updated NoScript status for security slider');
-  } catch(e) {
-      torbutton_log(4, 'Failed to update NoScript status for security slider: '+e);
-  }
-  torbutton_log(3, 'Security Slider Pref Update Complete');
-  m_tb_sliderUpdate = false;
-}
-
-// The user (re)set a pref which is relevant to the security slider while she
-// was in custom mode. Check whether the preference values fit to the mode which
-// is still on the slider. Iff so, we are leaving the custom mode.
-function torbutton_security_slider_custom_check(mode) {
-  let capValue = m_tb_prefs.getCharPref("capability.policy.maonoscript.sites");
-  switch (mode) {
-    case 1:
-      for (p in torbutton_sec_ml_bool_prefs) {
-        if (m_tb_prefs.getBoolPref(p) !== torbutton_sec_ml_bool_prefs[p]) {
-          return;
-        }
-      }
-      for (p in torbutton_sec_mh_bool_prefs) {
-        if (m_tb_prefs.getBoolPref(p) !== torbutton_sec_mh_bool_prefs[p]) {
-          // We don't want to have the whitelist in high mode. JavaScript is
-          // disabled globally.
-          if (p === "noscript.globalHttpsWhitelist") {
-            continue;
-          }
-          return;
-        }
-      }
-      for (p in torbutton_sec_h_bool_prefs) {
-        if (m_tb_prefs.getBoolPref(p) !== torbutton_sec_h_bool_prefs[p]) {
-          return;
-        }
-      }
-      // We are still here which means all preferences are properly reset. Leave
-      // custom mode.
-      m_tb_prefs.setBoolPref("extensions.torbutton.security_custom", false);
-      break;
-    case 2:
-      for (p in torbutton_sec_ml_bool_prefs) {
-        if (m_tb_prefs.getBoolPref(p) !== torbutton_sec_ml_bool_prefs[p]) {
-          return;
-        }
-      }
-      for (p in torbutton_sec_mh_bool_prefs) {
-        if (m_tb_prefs.getBoolPref(p) !== torbutton_sec_mh_bool_prefs[p]) {
-          return;
-        }
-      }
-      for (p in torbutton_sec_h_bool_prefs) {
-        if (m_tb_prefs.getBoolPref(p) === torbutton_sec_h_bool_prefs[p]) {
-          // We have the whitelist and JavaScript is disabled in medium-high
-          // mode as well.
-          if (p === "noscript.global") {
-            continue;
-          }
-          return;
-        }
-      }
-      // We are still here which means all preferences are properly reset. Leave
-      // custom mode.
-      m_tb_prefs.setBoolPref("extensions.torbutton.security_custom", false);
-      break;
-    case 3:
-      for (p in torbutton_sec_ml_bool_prefs) {
-        if (m_tb_prefs.getBoolPref(p) !== torbutton_sec_ml_bool_prefs[p]) {
-          return;
-        }
-      }
-      for (p in torbutton_sec_mh_bool_prefs) {
-        if (m_tb_prefs.getBoolPref(p) === torbutton_sec_mh_bool_prefs[p]) {
-          return;
-        }
-      }
-      for (p in torbutton_sec_h_bool_prefs) {
-        if (m_tb_prefs.getBoolPref(p) === torbutton_sec_h_bool_prefs[p]) {
-          return;
-        }
-      }
-      // We are still here which means all preferences are properly reset. Leave
-      // custom mode.
-      m_tb_prefs.setBoolPref("extensions.torbutton.security_custom", false);
-      break;
-    case 4:
-      for (p in torbutton_sec_ml_bool_prefs) {
-        if (m_tb_prefs.getBoolPref(p) === torbutton_sec_ml_bool_prefs[p]) {
-          return;
-        }
-      }
-      for (p in torbutton_sec_mh_bool_prefs) {
-        if (m_tb_prefs.getBoolPref(p) === torbutton_sec_mh_bool_prefs[p]) {
-          return;
-        }
-      }
-      for (p in torbutton_sec_h_bool_prefs) {
-        if (m_tb_prefs.getBoolPref(p) === torbutton_sec_h_bool_prefs[p]) {
-          return;
-        }
-      }
-      // We are still here which means all preferences are properly reset. Leave
-      // custom mode.
-      m_tb_prefs.setBoolPref("extensions.torbutton.security_custom", false);
-      break;
-  }
-}
-
 function torbutton_close_tabs_on_new_identity() {
     var close_newnym = m_tb_prefs.getBoolPref("extensions.torbutton.close_newnym");
     if (!close_newnym) {
@@ -2829,4 +2563,27 @@ function torbutton_update_sync_ui()
   }
 }
 
+// Update the NoScript button to reflect any changes to noscript prefs
+function torbutton_update_noscript_button()
+{
+  // Make sure pref values have fully propagated inside NoScript before
+  // we sync the UI.
+  setTimeout(() => {
+    try {
+      let wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
+          .getService(Components.interfaces.nsIWindowMediator);
+      let browserEnumerator = wm.getEnumerator("navigator:browser");
+      // Update every window's NoScript status...
+      while (browserEnumerator.hasMoreElements()) {
+        let win = browserEnumerator.getNext();
+        win.noscriptOverlay._syncUINow();
+      }
+      torbutton_log(3, 'Updated NoScript status for security settings');
+    } catch (e) {
+      torbutton_log(4, 'Failed to update NoScript status for security setings: '+e);
+    }
+  }, 0);
+}
+
+
 //vim:set ts=4
diff --git a/src/chrome/locale/en/torbutton.dtd b/src/chrome/locale/en/torbutton.dtd
index 56f663e..0c10406 100644
--- a/src/chrome/locale/en/torbutton.dtd
+++ b/src/chrome/locale/en/torbutton.dtd
@@ -11,7 +11,9 @@
 <!ENTITY torbutton.context_menu.cookieProtections "Cookie Protections…">
 <!ENTITY torbutton.context_menu.cookieProtections.key "C">
 <!ENTITY torbutton.button.tooltip "Click to initialize Torbutton">
-<!ENTITY torbutton.prefs.security_settings "Security Settings">
+<!ENTITY torbutton.prefs.security_settings "Tor Browser Security Settings">
+<!ENTITY torbutton.prefs.restore_defaults "Restore Defaults">
+<!ENTITY torbutton.prefs.custom_warning "Your custom browser preferences have resulted in unusual security settings. For security and privacy reasons, we recommend you choose one of the default security levels.">
 <!ENTITY torbutton.cookiedialog.title "Manage Cookie Protections">
 <!ENTITY torbutton.cookiedialog.lockCol "Protected">
 <!ENTITY torbutton.cookiedialog.domainCol "Host">
@@ -57,5 +59,4 @@
 <!ENTITY torbutton.prefs.sec_all_js_desc "JavaScript is disabled by default on all sites.">
 <!ENTITY torbutton.prefs.sec_webfonts_desc "Some fonts and icons may display incorrectly.">
 <!ENTITY torbutton.prefs.sec_webfonts_desc_tooltip "Website-provided font files are blocked.">
-<!ENTITY torbutton.prefs.sec_custom "Custom Values">
 <!ENTITY torbutton.circuit_display.title "Tor circuit for this site">
diff --git a/src/modules/security-prefs.js b/src/modules/security-prefs.js
new file mode 100644
index 0000000..3710862
--- /dev/null
+++ b/src/modules/security-prefs.js
@@ -0,0 +1,130 @@
+// # Security Settings prefs (as controlled by the Security Slider)
+
+// ### Utilities
+
+let {classes: Cc, utils: Cu } = Components;
+let { getBoolPref, setBoolPref, getIntPref, setIntPref } =
+    Cu.import("resource://gre/modules/Services.jsm").Services.prefs;
+let { bindPref } =
+    Cu.import("resource://torbutton/modules/utils.js");
+let logger = Components.classes["@torproject.org/torbutton-logger;1"]
+    .getService(Components.interfaces.nsISupports).wrappedJSObject;
+let log = (level, msg) => logger.log(level, msg);
+
+// ### Constants
+
+// __kSecuritySettings__.
+// A table of all prefs bound to the security slider, and the value
+// for each security setting.
+const kSecuritySettings = {
+  // Preference name :                        [0, 1-high 2-mh   3-ml   4-low]
+  "javascript.options.ion.content" :          [,  false, false, false, true ],
+  "javascript.options.typeinference" :        [,  false, false, false, true ],
+  "noscript.forbidMedia" :                    [,  true,  true,  true,  false],
+  "media.webaudio.enabled" :                  [,  false, false, false, true ],
+  "mathml.disabled" :                         [,  true,  true,  true,  false],
+  "javascript.options.baselinejit.content" :  [,  false, false, true,  true ],
+  "gfx.font_rendering.opentype_svg.enabled" : [,  false, false, true,  true ],
+  "noscript.global" :                         [,  false, false, true,  true ],
+  "noscript.globalHttpsWhitelist" :           [,  false, true,  false, false],
+  "noscript.forbidFonts" :                    [,  true,  false, false, false],
+  "svg.in-content.enabled" :                  [,  false, true,  true,  true ],
+};
+
+// The Security Settings prefs in question.
+const kSliderPref = "extensions.torbutton.security_slider";
+const kCustomPref = "extensions.torbutton.security_custom";
+
+// ### Prefs
+
+// __write_setting_to_prefs(settingIndex)__.
+// Take a given setting index and write the appropriate pref values
+// to the pref database.
+var write_setting_to_prefs = function (settingIndex) {
+  Object.keys(kSecuritySettings).forEach(
+    prefName => setBoolPref(
+      prefName, kSecuritySettings[prefName][settingIndex]));
+};
+
+// __read_setting_from_prefs()__.
+// Read the current pref values, and decide if any of our
+// security settings matches. Otherwise return null.
+var read_setting_from_prefs = function () {
+  let prefNames = Object.keys(kSecuritySettings);
+  for (let settingIndex of [1, 2, 3, 4]) {
+    let possibleSetting = true;
+    // For the given settingIndex, check if all current pref values
+    // match the setting.
+    for (let prefName of prefNames) {
+      if (kSecuritySettings[prefName][settingIndex] !==
+          getBoolPref(prefName)) {
+        possibleSetting = false;
+      }
+    }
+    if (possibleSetting) {
+      // We have a match!
+      return settingIndex;
+    }
+  }
+  // No matching setting; return null.
+  return null;
+};
+
+// __watch_security_prefs(onSettingChanged)__.
+// Whenever a pref bound to the security slider changes, onSettingChanged
+// is called with the new security setting value (1,2,3,4 or null).
+// Returns a zero-arg function that ends this binding.
+var watch_security_prefs = function (onSettingChanged) {
+  let prefNames = Object.keys(kSecuritySettings);
+  let unbindFuncs = [];
+  for (let prefName of prefNames) {
+    unbindFuncs.push(bindPref(
+      prefName, () => onSettingChanged(read_setting_from_prefs())));
+  }
+  // Call all the unbind functions.
+  return () => unbindFuncs.forEach(unbind => unbind());
+};
+
+// __initialized__.
+// Have we called initialize() yet?
+var initialized = false;
+
+// __initialize()__.
+// Defines the behavior of "extensions.torbutton.security_custom",
+// "extensions.torbutton.security_slider", and the security-sensitive
+// prefs declared in kSecuritySettings.
+var initialize = function () {
+  // Only run once.
+  if (initialized) {
+    return;
+  }
+  log(4, "Initializing security-prefs.js");
+  initialized = true;
+  // When security_custom is set to false, apply security_slider setting
+  // to the security-sensitive prefs.
+  bindPref(kCustomPref, function (custom) {
+    if (custom === false) {
+      write_setting_to_prefs(getIntPref(kSliderPref));
+    }
+  });
+  // If security_slider is given a new value, then security_custom should
+  // be set to false.
+  bindPref(kSliderPref, function (prefIndex) {
+    setBoolPref(kCustomPref, false);
+    write_setting_to_prefs(prefIndex);
+  });
+  // If a security-sensitive pref changes, then decide if the set of pref values
+  // constitutes a security_slider setting or a custom value.
+  watch_security_prefs(settingIndex => {
+    if (settingIndex === null) {
+      setBoolPref(kCustomPref, true);
+    } else {
+      setIntPref(kSliderPref, settingIndex);
+      setBoolPref(kCustomPref, false);
+    }
+  });
+  log(4, "security-prefs.js initialization complete");
+};
+
+// Export initialize() function for external use.
+let EXPORTED_SYMBOLS = ["initialize"];
diff --git a/src/modules/utils.js b/src/modules/utils.js
index b303485..507f008 100644
--- a/src/modules/utils.js
+++ b/src/modules/utils.js
@@ -23,11 +23,11 @@ var getPrefValue = function (prefName) {
   }
 };
 
-// __bindPrefAndInit(prefName, prefHandler)__
-// Applies prefHandler to the current value of pref specified by prefName.
-// Re-applies prefHandler whenever the value of the pref changes.
+// __bindPref(prefName, prefHandler, init)__
+// Applies prefHandler whenever the value of the pref changes.
+// If init is true, applies prefHandler to the current value.
 // Returns a zero-arg function that unbinds the pref.
-var bindPrefAndInit = function (prefName, prefHandler) {
+var bindPref = function (prefName, prefHandler, init = false) {
   let update = () => { prefHandler(getPrefValue(prefName)); },
       observer = { observe : function (subject, topic, data) {
                      if (data === prefName) {
@@ -35,10 +35,19 @@ var bindPrefAndInit = function (prefName, prefHandler) {
                      }
                    } };
   prefs.addObserver(prefName, observer, false);
-  update();
+  if (init) {
+    update();
+  }
   return () => { prefs.removeObserver(prefName, observer); };
 };
 
+// __bindPrefAndInit(prefName, prefHandler)__
+// Applies prefHandler to the current value of pref specified by prefName.
+// Re-applies prefHandler whenever the value of the pref changes.
+// Returns a zero-arg function that unbinds the pref.
+var bindPrefAndInit = (prefName, prefHandler) =>
+    bindPref(prefName, prefHandler, true);
+
 // ## Environment variables
 
 // __env__.
@@ -173,5 +182,5 @@ var unescapeTorString = function(str) {
 };
 
 // Export utility functions for external use.
-let EXPORTED_SYMBOLS = ["bindPrefAndInit", "getPrefValue", "getEnv",
+let EXPORTED_SYMBOLS = ["bindPref", "bindPrefAndInit", "getEnv", "getPrefValue",
                         "showDialog", "unescapeTorString"];



More information about the tor-commits mailing list