bug 436077 - Preferences
authorDaniel Brooks <db48x@db48x.net>
Sun, 07 Sep 2008 01:54:06 -0500
changeset 64858 abd6da89759f54c0980dd02324d98a17419165a1
parent 64814 838eb8c4f21b5e1255510dce1dea8a8cc4726d31
child 64859 e50b8ce159d0c4d9182098b3808deb0140de09fc
push id19389
push userffxbld
push dateWed, 06 Apr 2011 21:33:21 +0000
treeherdermozilla-central@8e9f90073a20 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs436077
bug 436077 - Preferences
mobile/app/mobile.js
mobile/chrome/content/browser-ui.js
mobile/chrome/content/browser.css
mobile/chrome/content/browser.xul
mobile/chrome/content/preferences/richpref.xml
mobile/chrome/content/sanitize.js
mobile/chrome/content/sanitize.xul
mobile/chrome/jar.mn
mobile/chrome/locale/en-US/preferences.dtd
mobile/chrome/skin/browser.css
mobile/chrome/skin/richpref.css
mobile/chrome/skin/section.css
--- a/mobile/app/mobile.js
+++ b/mobile/app/mobile.js
@@ -223,8 +223,19 @@ pref("places.frecency.linkVisitBonus", 1
 pref("places.frecency.typedVisitBonus", 2000);
 pref("places.frecency.bookmarkVisitBonus", 150);
 pref("places.frecency.downloadVisitBonus", 0);
 pref("places.frecency.permRedirectVisitBonus", 0);
 pref("places.frecency.tempRedirectVisitBonus", 0);
 pref("places.frecency.defaultVisitBonus", 0);
 pref("places.frecency.unvisitedBookmarkBonus", 140);
 pref("places.frecency.unvisitedTypedBonus", 200);
+
+// controls which bits of private data to clear. by default we clear them all.
+pref("privacy.sanitize.promptOnSanitize", false);
+pref("privacy.item.cache", true);
+pref("privacy.item.cookies", true);
+pref("privacy.item.offlineApps", true);
+pref("privacy.item.history", true);
+pref("privacy.item.formdata", true);
+pref("privacy.item.downloads", true);
+pref("privacy.item.passwords", true);
+pref("privacy.item.sessions", true);
--- a/mobile/chrome/content/browser-ui.js
+++ b/mobile/chrome/content/browser-ui.js
@@ -1,8 +1,9 @@
+// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
 /* ***** BEGIN LICENSE BLOCK *****
  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  *
  * The contents of this file are subject to the Mozilla Public License Version
  * 1.1 (the "License"); you may not use this file except in compliance with
  * the License. You may obtain a copy of the License at
  * http://www.mozilla.org/MPL/
  *
@@ -43,16 +44,17 @@ const PANELMODE_NONE              = 0;
 const PANELMODE_URLVIEW           = 1;
 const PANELMODE_URLEDIT           = 2;
 const PANELMODE_BOOKMARK          = 3;
 const PANELMODE_BOOKMARKLIST      = 4;
 const PANELMODE_ADDONS            = 5;
 const PANELMODE_SIDEBAR           = 6;
 const PANELMODE_TABLIST           = 7;
 const PANELMODE_FULL              = 8;
+const PANELMODE_PREFS             = 9;
 
 const kDefaultFavIconURL = "chrome://browser/skin/images/default-favicon.png";
 
 var BrowserUI = {
   _panel : null,
   _caption : null,
   _edit : null,
   _throbber : null,
@@ -447,28 +449,34 @@ var BrowserUI = {
 
     var toolbar = document.getElementById("toolbar-main");
     var bookmark = document.getElementById("bookmark-container");
     var urllist = document.getElementById("urllist-container");
     var sidebar = document.getElementById("browser-controls");
     var tablist = document.getElementById("tab-list-container");
     var addons = document.getElementById("addons-container");
     var container = document.getElementById("browser-container");
+    var prefs = document.getElementById("pref-pane");
+
+    // Make sure the UI elements are sized correctly since the window size can change
+    sidebar.left = container.boxObject.width;
+    sidebar.height = tablist.height = container.boxObject.height;
 
     if (aMode == PANELMODE_URLVIEW || aMode == PANELMODE_SIDEBAR ||
         aMode == PANELMODE_TABLIST || aMode == PANELMODE_FULL)
     {
       this._showToolbar();
       toolbar.setAttribute("mode", "view");
       this._edit.hidden = true;
       this._edit.reallyClosePopup();
       this._caption.hidden = false;
       bookmark.hidden = true;
       urllist.hidden = true;
       addons.hidden = true;
+      prefs.hidden = true;
 
       let sidebarTo = toolbar.boxObject.width;
       let tablistTo = -tablist.boxObject.width;
       if (aMode == PANELMODE_SIDEBAR || aMode == PANELMODE_FULL)
         sidebarTo -= sidebar.boxObject.width;
       if (aMode == PANELMODE_TABLIST || aMode == PANELMODE_FULL)
         tablistTo = 0;
       sidebar.left = sidebarTo;
@@ -479,78 +487,106 @@ var BrowserUI = {
       toolbar.setAttribute("mode", "edit");
       this._caption.hidden = true;
       this._edit.hidden = false;
       this._edit.focus();
 
       bookmark.hidden = true;
       urllist.hidden = true;
       addons.hidden = true;
+      prefs.hidden = true;
+
       sidebar.left = toolbar.boxObject.width;
       tablist.left = -tablist.boxObject.width;
     }
     else if (aMode == PANELMODE_BOOKMARK) {
       this._showToolbar();
       toolbar.setAttribute("mode", "view");
       this._edit.hidden = true;
       this._edit.reallyClosePopup();
       this._caption.hidden = false;
 
       urllist.hidden = true;
       sidebar.left = toolbar.boxObject.width;
       tablist.left = -tablist.boxObject.width;
 
       bookmark.hidden = false;
       addons.hidden = true;
+      prefs.hidden = true;
+
       bookmark.width = container.boxObject.width;
     }
     else if (aMode == PANELMODE_BOOKMARKLIST) {
       this._showToolbar();
       toolbar.setAttribute("mode", "view");
       this._edit.hidden = true;
       this._edit.reallyClosePopup();
       this._caption.hidden = false;
 
       bookmark.hidden = true;
       addons.hidden = true;
+      prefs.hidden = true;
+
       sidebar.left = toolbar.boxObject.width;
       tablist.left = -tablist.boxObject.width;
 
       urllist.hidden = false;
       urllist.width = container.boxObject.width;
       urllist.height = container.boxObject.height;
     }
     else if (aMode == PANELMODE_ADDONS) {
       this._showToolbar();
       toolbar.setAttribute("mode", "view");
       this._edit.hidden = true;
       this._edit.reallyClosePopup();
       this._caption.hidden = false;
 
       bookmark.hidden = true;
+      prefs.hidden = true;
       sidebar.left = toolbar.boxObject.width;
       tablist.left = -tablist.boxObject.width;
 
       var iframe = document.getElementById("addons-items-container");
       if (iframe.getAttribute("src") == "")
         iframe.setAttribute("src", "chrome://mozapps/content/extensions/extensions.xul");
 
       addons.hidden = false;
       addons.width = container.boxObject.width;
       addons.height = container.boxObject.height - toolbar.boxObject.height;
     }
-    else if (aMode == PANELMODE_NONE) {
+    else if (aMode == PANELMODE_PREFS) {
+      this._showToolbar();
+      toolbar.setAttribute("mode", "view");
+      this._edit.hidden = true;
+      this._edit.reallyClosePopup();
+      this._caption.hidden = false;
+
+      bookmark.hidden = true;
+      urllist.hidden = true;
+      addons.hidden = true;
+      prefs.hidden = false;
+      sidebar.left = toolbar.boxObject.width;
+      tablist.left = -tablist.boxObject.width;
+
+      prefs.width = container.boxObject.width;
+      prefs.height = container.boxObject.height - toolbar.boxObject.height;
+
+      PreferencesUI.init();
+    }
+      else if (aMode == PANELMODE_NONE) {
       this._hideToolbar();
+
       sidebar.left = toolbar.boxObject.width;
       tablist.left = -tablist.boxObject.width;
 
       this._edit.reallyClosePopup();
       urllist.hidden = true;
       bookmark.hidden = true;
       addons.hidden = true;
+      prefs.hidden = true;
     }
   },
 
   _showPlaces : function(aItems) {
     var list = document.getElementById("urllist-items");
     while (list.firstChild) {
       list.removeChild(list.firstChild);
     }
@@ -680,16 +716,18 @@ var BrowserUI = {
       case "cmd_go":
       case "cmd_star":
       case "cmd_bookmarks":
       case "cmd_menu":
       case "cmd_newTab":
       case "cmd_closeTab":
       case "cmd_addons":
       case "cmd_actions":
+      case "cmd_prefs":
+      case "cmd_sanitize":
         isSupported = true;
         break;
       default:
         isSupported = false;
         break;
     }
     return isSupported;
   },
@@ -745,30 +783,36 @@ var BrowserUI = {
         }
         break;
       }
       case "cmd_bookmarks":
         this.showBookmarks();
         break;
       case "cmd_menu":
         // XXX Remove PANELMODE_ADDON when design changes
-        if (this.mode == PANELMODE_FULL || this.mode == PANELMODE_ADDONS)
+        if (this.mode == PANELMODE_FULL || this.mode == PANELMODE_ADDONS || this.mode == PANELMODE_PREFS)
           this.show(PANELMODE_NONE);
         else
           this.show(PANELMODE_FULL);
         break;
       case "cmd_newTab":
         this.newTab();
         break;
       case "cmd_closeTab":
         Browser.content.removeTab(Browser.content.browser);
         break;
       case "cmd_addons":
+        this.show(PANELMODE_ADDONS);
+        break;
+      case "cmd_prefs":
       case "cmd_actions":
-        this.show(PANELMODE_ADDONS);
+        this.show(PANELMODE_PREFS);
+        break;
+      case "cmd_sanitize":
+        Sanitizer.sanitize();
         break;
     }
   }
 };
 
 var BookmarkHelper = {
   _item : null,
   _uri : null,
--- a/mobile/chrome/content/browser.css
+++ b/mobile/chrome/content/browser.css
@@ -4,8 +4,24 @@ deckbrowser {
 
 #urlbar-edit {
     -moz-binding: url("chrome://browser/content/urlbar.xml#autocomplete-aligned");
 }
 
 richlistitem[type="documenttab"] {
     -moz-binding: url("chrome://browser/content/deckbrowser.xml#documenttab");
 }
+
+richpreflist {
+    -moz-binding: url("chrome://browser/content/preferences/richpref.xml#richpreflist");
+}
+
+richpref[type="bool"] {
+    -moz-binding: url("chrome://browser/content/preferences/richpref.xml#richpref-bool");
+}
+
+richpref[type="boolint"] {
+    -moz-binding: url("chrome://browser/content/preferences/richpref.xml#richpref-boolint");
+}
+
+richpref[type="button"] {
+    -moz-binding: url("chrome://browser/content/preferences/richpref.xml#richpref-button");
+}
--- a/mobile/chrome/content/browser.xul
+++ b/mobile/chrome/content/browser.xul
@@ -35,37 +35,41 @@
    - and other provisions required by the LGPL or the GPL. If you do not delete
    - the provisions above, a recipient may use your version of this file under
    - the terms of any one of the MPL, the GPL or the LGPL.
    -
    - ***** END LICENSE BLOCK ***** -->
 
 <?xml-stylesheet href="chrome://browser/skin/browser.css" type="text/css"?>
 <?xml-stylesheet href="chrome://browser/content/browser.css" type="text/css"?>
+<?xml-stylesheet href="chrome://browser/skin/richpref.css" type="text/css"?>
 
 <!DOCTYPE window [
 <!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd">
 %browserDTD;
 <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
 %brandDTD;
+<!ENTITY % prefsDTD SYSTEM "chrome://browser/locale/preferences.dtd">
+%prefsDTD;
 ]>
 
 <window id="main-window"
         width="800" height="480"
         onload="Browser.startup();"
         windowtype="navigator:browser"
         title="&brandShortName;"
         titlemodifier="&brandShortName;"
         titleseparator="&mainWindow.titleseparator;"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
   <script type="application/x-javascript" src="chrome://global/content/inlineSpellCheckUI.js"/>
   <script type="application/x-javascript" src="chrome://browser/content/commandUtil.js"/>
   <script type="application/x-javascript" src="chrome://browser/content/browser.js"/>
   <script type="application/x-javascript" src="chrome://browser/content/browser-ui.js"/>
+  <script type="application/x-javascript" src="chrome://browser/content/sanitize.js"/>
 
   <stringbundleset id="stringbundleset">
     <stringbundle id="bundle_browser"  src="chrome://browser/locale/browser.properties"/>
     <stringbundle id="bundle_brand"  src="chrome://branding/locale/brand.properties"/>
   </stringbundleset>
 
   <commandset id="cmdset_main">
     <!-- basic navigation -->
@@ -85,16 +89,17 @@
     <command id="cmd_bookmarks" label="&bookmarks.label;" oncommand="CommandUpdater.doCommand(this.id);"/>
 
     <!-- misc -->
     <command id="cmd_menu" oncommand="CommandUpdater.doCommand(this.id);"/>
     <command id="cmd_fullscreen" oncommand="CommandUpdater.doCommand(this.id);"/>
     <command id="cmd_addons" oncommand="CommandUpdater.doCommand(this.id);"/>
     <command id="cmd_downloads" oncommand="CommandUpdater.doCommand(this.id);"/>
     <command id="cmd_actions" oncommand="CommandUpdater.doCommand(this.id);"/>
+    <command id="cmd_sanitize" oncommand="CommandUpdater.doCommand(this.id);"/>
 
     <!-- scrolling -->
     <command id="cmd_scrollPageUp" oncommand="CommandUpdater.doCommand(this.id);"/>
     <command id="cmd_scrollPageDown" oncommand="CommandUpdater.doCommand(this.id);"/>
 
     <!-- editing -->
     <command id="cmd_cut" label="&cut.label;" oncommand="CommandUpdater.doCommand(this.id);"/>
     <command id="cmd_copy" label="&copy.label;" oncommand="CommandUpdater.doCommand(this.id);"/>
@@ -266,16 +271,49 @@
           <button label="&bookmarkDone.label;" oncommand="BookmarkHelper.save()"/>
         </hbox>
       </vbox>
     </vbox>
 
     <vbox id="addons-container" hidden="true" style="-moz-stack-sizing: ignore;" top="60" left="0">
       <iframe id="addons-items-container" flex="1" src=""/>
     </vbox>
+
+    <vbox id="pref-pane" hidden="true" style="-moz-stack-sizing: ignore;" top="60" left="0">
+      <hbox pack="center" id="buttons"/>
+      <richlistbox id="pref-list" seltype="single" flex="1">
+        <richlistitem class="section">
+          <label class="sectiontitle" value="&content.title;" crop="end" flex="1"/>
+        </richlistitem>
+        <richpref pref="permissions.default.image" title="&permissions.default.image.title;" type="boolint" on="1" off="2">
+          &permissions.default.image.description;
+        </richpref>
+        <richpref pref="javascript.enabled" type="bool" title="&javascript.enabled.title;">
+          &javascript.enabled.description;
+        </richpref>
+        <richpref pref="plugins.enabled" type="bool" title="&plugins.enabled.title;">
+          &plugins.enabled.description;
+        </richpref>
+
+        <richlistitem class="section">
+          <label class="sectiontitle" value="&privacy.title;" crop="end" flex="1"/>
+        </richlistitem>
+        <richpref pref="network.cookie.cookieBehavior" title="&network.cookie.cookieBehavior.title;" type="boolint" on="1" off="3">
+          &network.cookie.cookieBehavior.description;
+        </richpref>
+        <richpref title="&clear.private.data.title;" type="button">
+          &clear.private.data.description;
+          <button label="&clear.private.data.button;" command="cmd_sanitize"/>
+        </richpref>
+
+      </richlistbox>
+      <hbox pack="end">
+        <button label="Dismiss" oncommand="BrowserUI.show(PANELMODE_NONE);"/>
+      </hbox>
+    </vbox>
   </stack>
 
   <vbox id="findpanel-placeholder" sizetopopup="always">
     <panel id="findpanel" onpopupshown="Browser.doFind()">
       <findbar id="findbar"/>
    </panel>
   </vbox>
 
new file mode 100644
--- /dev/null
+++ b/mobile/chrome/content/preferences/richpref.xml
@@ -0,0 +1,145 @@
+<?xml version="1.0"?>
+<!-- ***** BEGIN LICENSE BLOCK *****
+   - Version: MPL 1.1/GPL 2.0/LGPL 2.1
+   -
+   - The contents of this file are subject to the Mozilla Public License Version
+   - 1.1 (the "License"); you may not use this file except in compliance with
+   - the License. You may obtain a copy of the License at
+   - http://www.mozilla.org/MPL/
+   -
+   - Software distributed under the License is distributed on an "AS IS" basis,
+   - WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+   - for the specific language governing rights and limitations under the
+   - License.
+   -
+   - The Original Code is Mozilla Mobile Browser.
+   -
+   - The Initial Developer of the Original Code is
+   - Mozilla Corporation.
+   - Portions created by the Initial Developer are Copyright (C) 2008
+   - the Initial Developer. All Rights Reserved.
+   -
+   - Contributor(s):
+   -   Daniel Brooks <db48x@yahoo.com>
+   -
+   - Alternatively, the contents of this file may be used under the terms of
+   - either the GNU General Public License Version 2 or later (the "GPL"), or
+   - the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+   - in which case the provisions of the GPL or the LGPL are applicable instead
+   - of those above. If you wish to allow use of your version of this file only
+   - under the terms of either the GPL or the LGPL, and not to allow others to
+   - use your version of this file under the terms of the MPL, indicate your
+   - decision by deleting the provisions above and replace them with the notice
+   - and other provisions required by the LGPL or the GPL. If you do not delete
+   - the provisions above, a recipient may use your version of this file under
+   - the terms of any one of the MPL, the GPL or the LGPL.
+   -
+   - ***** END LICENSE BLOCK ***** -->
+<!DOCTYPE bindings PUBLIC "-//MOZILLA//DTD XBL V1.0//EN" "http://www.mozilla.org/xbl">
+
+<bindings xmlns="http://www.mozilla.org/xbl"
+          xmlns:xbl="http://www.mozilla.org/xbl"
+          xmlns:html="http://www.w3.org/1999/xhtml"
+          xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+  <binding id="richpref-base" extends="chrome://global/content/bindings/richlistbox.xml#richlistitem">
+    <implementation>
+      <constructor>
+        this.pref._setValue(this.pref.valueFromPreferences, false);
+        this.prefChanged();
+      </constructor>
+
+      <method name="inputChanged">
+        <body>
+          this.pref.value = this.value;
+        </body>
+      </method>
+
+      <method name="prefChanged">
+        <body>
+          this.value = this.pref.value;
+        </body>
+      </method>
+
+      <property name="value" onget="return this.input.value;" onset="return this.input.value = val;"/>
+      <field name="type">this.getAttribute("type");</field>
+      <field name="pref">document.getAnonymousElementByAttribute(this, "anonid", "pref");</field>
+      <field name="input">document.getAnonymousElementByAttribute(this, "anonid", "input");</field>
+    </implementation>
+  </binding>
+
+  <binding id="richpref-bool" extends="chrome://browser/content/preferences/richpref.xml#richpref-base">
+    <content>
+      <xul:hbox flex="1" class="prefbox">
+        <xul:vbox flex="1">
+          <xul:label class="preftitle" xbl:inherits="value=title" crop="end" flex="1"/>
+          <xul:label class="prefdesc" xbl:inherits="value=desc" crop="end" flex="1">
+            <children/>
+          </xul:label>
+        </xul:vbox>
+        <xul:hbox anonid="input-container">
+          <xul:checkbox anonid="input" oncommand="inputChanged();"/>
+        </xul:hbox>
+      </xul:hbox>
+      <xul:preferences>
+        <xul:preference anonid="pref" xbl:inherits="name=pref,type,inverted" instantApply="true" onchange="prefChanged();"/>
+      </xul:preferences>
+    </content>
+    <implementation>
+      <property name="value" onget="return this.input.checked;" onset="return this.input.setChecked(val);"/>
+    </implementation>
+  </binding>
+
+  <binding id="richpref-boolint" extends="chrome://browser/content/preferences/richpref.xml#richpref-base">
+    <content>
+      <xul:hbox flex="1" class="prefbox">
+        <xul:vbox flex="1">
+          <xul:label class="preftitle" xbl:inherits="value=title" crop="end" flex="1"/>
+          <xul:label class="prefdesc" xbl:inherits="value=desc" crop="end" flex="1">
+            <children/>
+          </xul:label>
+        </xul:vbox>
+        <xul:hbox anonid="input-container">
+          <xul:checkbox anonid="input" oncommand="inputChanged();"/>
+        </xul:hbox>
+      </xul:hbox>
+      <xul:preferences>
+        <xul:preference anonid="pref" xbl:inherits="name=pref,inverted" type="int" instantApply="true" onchange="prefChanged();"/>
+      </xul:preferences>
+    </content>
+    <implementation>
+      <method name="inputChanged">
+        <body>
+          this.pref.value = this.getAttribute(this.value ? "on" : "off");
+        </body>
+      </method>
+
+      <method name="prefChanged">
+        <body>
+          this.value = this.pref.value == this.getAttribute("on");
+        </body>
+      </method>
+
+      <property name="value" onget="return this.input.checked;" onset="return this.input.setChecked(val);"/>
+    </implementation>
+  </binding>
+
+  <binding id="richpref-button" extends="chrome://browser/content/preferences/richpref.xml#richpref-base">
+    <content>
+      <xul:hbox flex="1" class="prefbox">
+        <xul:vbox flex="1">
+          <xul:label class="preftitle" xbl:inherits="value=title" crop="end" flex="1"/>
+          <xul:label class="prefdesc" xbl:inherits="value=desc" crop="end" flex="1">
+            <children/>
+          </xul:label>
+        </xul:vbox>
+        <xul:hbox anonid="input-container">
+          <children includes="button"/>
+        </xul:hbox>
+      </xul:hbox>
+      <xul:preferences>
+        <xul:preference anonid="pref" xbl:inherits="name=pref,type,inverted" instantApply="true" onchange="prefChanged();"/>
+      </xul:preferences>
+    </content>
+  </binding>
+</bindings>
new file mode 100644
--- /dev/null
+++ b/mobile/chrome/content/sanitize.js
@@ -0,0 +1,336 @@
+# -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is the Firefox Sanitizer.
+#
+# The Initial Developer of the Original Code is
+# Ben Goodger.
+# Portions created by the Initial Developer are Copyright (C) 2005
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#   Ben Goodger <ben@mozilla.org>
+#   Giorgio Maone <g.maone@informaction.com>
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+function Sanitizer() {}
+Sanitizer.prototype = {
+  // warning to the caller: this one may raise an exception (e.g. bug #265028)
+  clearItem: function (aItemName)
+  {
+    if (this.items[aItemName].canClear)
+      this.items[aItemName].clear();
+  },
+
+  canClearItem: function (aItemName)
+  {
+    return this.items[aItemName].canClear;
+  },
+  
+  _prefDomain: "privacy.item.",
+  getNameFromPreference: function (aPreferenceName)
+  {
+    return aPreferenceName.substr(this._prefDomain.length);
+  },
+  
+  /**
+   * Deletes privacy sensitive data in a batch, according to user preferences
+   *
+   * @returns  null if everything's fine;  an object in the form
+   *           { itemName: error, ... } on (partial) failure
+   */
+  sanitize: function ()
+  {
+    var psvc = Components.classes["@mozilla.org/preferences-service;1"]
+                         .getService(Components.interfaces.nsIPrefService);
+    var branch = psvc.getBranch(this._prefDomain);
+    var errors = null;
+    for (var itemName in this.items) {
+      var item = this.items[itemName];
+      if ("clear" in item && item.canClear && branch.getBoolPref(itemName)) {
+        // Some of these clear() may raise exceptions (see bug #265028)
+        // to sanitize as much as possible, we catch and store them, 
+        // rather than fail fast.
+        // Callers should check returned errors and give user feedback
+        // about items that could not be sanitized
+        try {
+          item.clear();
+        } catch(er) {
+          if (!errors) 
+            errors = {};
+          errors[itemName] = er;
+          dump("Error sanitizing " + itemName + ": " + er + "\n");
+        }
+      }
+    }
+    return errors;
+  },
+  
+  items: {
+    cache: {
+      clear: function ()
+      {
+        const cc = Components.classes;
+        const ci = Components.interfaces;
+        var cacheService = cc["@mozilla.org/network/cache-service;1"]
+                             .getService(ci.nsICacheService);
+        try {
+          cacheService.evictEntries(ci.nsICache.STORE_ANYWHERE);
+        } catch(er) {}
+      },
+      
+      get canClear()
+      {
+        return true;
+      }
+    },
+    
+    cookies: {
+      clear: function ()
+      {
+        var cookieMgr = Components.classes["@mozilla.org/cookiemanager;1"]
+                                  .getService(Components.interfaces.nsICookieManager);
+        cookieMgr.removeAll();
+      },
+      
+      get canClear()
+      {
+        return true;
+      }
+    },
+    
+    offlineApps: {
+      clear: function ()
+      {
+        const Cc = Components.classes;
+        const Ci = Components.interfaces;
+        var cacheService = Cc["@mozilla.org/network/cache-service;1"].
+                           getService(Ci.nsICacheService);
+        try {
+          cacheService.evictEntries(Ci.nsICache.STORE_OFFLINE);
+        } catch(er) {}
+
+        var storageManagerService = Cc["@mozilla.org/dom/storagemanager;1"].
+                                    getService(Ci.nsIDOMStorageManager);
+        storageManagerService.clearOfflineApps();
+      },
+
+      get canClear()
+      {
+          return true;
+      }
+    },
+
+    history: {
+      clear: function ()
+      {
+        var globalHistory = Components.classes["@mozilla.org/browser/global-history;2"]
+                                      .getService(Components.interfaces.nsIBrowserHistory);
+        globalHistory.removeAllPages();
+        
+        try {
+          var os = Components.classes["@mozilla.org/observer-service;1"]
+                             .getService(Components.interfaces.nsIObserverService);
+          os.notifyObservers(null, "browser:purge-session-history", "");
+        }
+        catch (e) { }
+        
+        // Clear last URL of the Open Web Location dialog
+        var prefs = Components.classes["@mozilla.org/preferences-service;1"]
+                              .getService(Components.interfaces.nsIPrefBranch2);
+        try {
+          prefs.clearUserPref("general.open_location.last_url");
+        }
+        catch (e) { }
+      },
+      
+      get canClear()
+      {
+        // bug 347231: Always allow clearing history due to dependencies on
+        // the browser:purge-session-history notification. (like error console)
+        return true;
+      }
+    },
+    
+    formdata: {
+      clear: function ()
+      {
+        //Clear undo history of all searchBars
+        var windowManager = Components.classes['@mozilla.org/appshell/window-mediator;1'].getService();
+        var windowManagerInterface = windowManager.QueryInterface(Components.interfaces.nsIWindowMediator);
+        var windows = windowManagerInterface.getEnumerator("navigator:browser");
+        while (windows.hasMoreElements()) {
+          var searchBar = windows.getNext().document.getElementById("searchbar");
+          if (searchBar) {
+            searchBar.value = "";
+            searchBar.textbox.editor.transactionManager.clear();
+          }
+        }
+
+        var formHistory = Components.classes["@mozilla.org/satchel/form-history;1"]
+                                    .getService(Components.interfaces.nsIFormHistory2);
+        formHistory.removeAllEntries();
+      },
+      
+      get canClear()
+      {
+        var formHistory = Components.classes["@mozilla.org/satchel/form-history;1"]
+                                    .getService(Components.interfaces.nsIFormHistory2);
+        return formHistory.hasEntries;
+      }
+    },
+    
+    downloads: {
+      clear: function ()
+      {
+        var dlMgr = Components.classes["@mozilla.org/download-manager;1"]
+                              .getService(Components.interfaces.nsIDownloadManager);
+        dlMgr.cleanUp();
+      },
+
+      get canClear()
+      {
+        var dlMgr = Components.classes["@mozilla.org/download-manager;1"]
+                              .getService(Components.interfaces.nsIDownloadManager);
+        return dlMgr.canCleanUp;
+      }
+    },
+    
+    passwords: {
+      clear: function ()
+      {
+        var pwmgr = Components.classes["@mozilla.org/login-manager;1"]
+                              .getService(Components.interfaces.nsILoginManager);
+        pwmgr.removeAllLogins();
+      },
+      
+      get canClear()
+      {
+        var pwmgr = Components.classes["@mozilla.org/login-manager;1"]
+                              .getService(Components.interfaces.nsILoginManager);
+        var count = pwmgr.countLogins("", "", ""); // count all logins
+        return (count > 0);
+      }
+    },
+    
+    sessions: {
+      clear: function ()
+      {
+        // clear all auth tokens
+        var sdr = Components.classes["@mozilla.org/security/sdr;1"]
+                            .getService(Components.interfaces.nsISecretDecoderRing);
+        sdr.logoutAndTeardown();
+
+        // clear plain HTTP auth sessions
+        var authMgr = Components.classes['@mozilla.org/network/http-auth-manager;1']
+                                .getService(Components.interfaces.nsIHttpAuthManager);
+        authMgr.clearAll();
+      },
+      
+      get canClear()
+      {
+        return true;
+      }
+    }
+  }
+};
+
+
+
+// "Static" members
+Sanitizer.prefDomain          = "privacy.sanitize.";
+Sanitizer.prefPrompt          = "promptOnSanitize";
+Sanitizer.prefShutdown        = "sanitizeOnShutdown";
+Sanitizer.prefDidShutdown     = "didShutdownSanitize";
+
+Sanitizer._prefs = null;
+Sanitizer.__defineGetter__("prefs", function() 
+{
+  return Sanitizer._prefs ? Sanitizer._prefs
+    : Sanitizer._prefs = Components.classes["@mozilla.org/preferences-service;1"]
+                         .getService(Components.interfaces.nsIPrefService)
+                         .getBranch(Sanitizer.prefDomain);
+});
+
+// Shows sanitization UI
+Sanitizer.showUI = function(aParentWindow) 
+{
+  var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
+                     .getService(Components.interfaces.nsIWindowWatcher);
+#ifdef XP_MACOSX
+  ww.openWindow(null, // make this an app-modal window on Mac
+#else
+  ww.openWindow(aParentWindow,
+#endif
+                "chrome://browser/content/sanitize.xul",
+                "Sanitize",
+                "chrome,titlebar,centerscreen,modal",
+                null);
+};
+
+/** 
+ * Deletes privacy sensitive data in a batch, optionally showing the 
+ * sanitize UI, according to user preferences
+ *
+ * @returns  null if everything's fine (no error or displayed UI,  which
+ *           should handle errors);  
+ *           an object in the form { itemName: error, ... } on (partial) failure
+ */
+Sanitizer.sanitize = function(aParentWindow) 
+{
+  if (Sanitizer.prefs.getBoolPref(Sanitizer.prefPrompt)) {
+    Sanitizer.showUI(aParentWindow);
+    return null;
+  }
+  return new Sanitizer().sanitize();
+};
+
+Sanitizer.onStartup = function() 
+{
+  // we check for unclean exit with pending sanitization
+  Sanitizer._checkAndSanitize();
+};
+
+Sanitizer.onShutdown = function() 
+{
+  // we check if sanitization is needed and perform it
+  Sanitizer._checkAndSanitize();
+};
+
+// this is called on startup and shutdown, to perform pending sanitizations
+Sanitizer._checkAndSanitize = function() 
+{
+  const prefs = Sanitizer.prefs;
+  if (prefs.getBoolPref(Sanitizer.prefShutdown) && 
+      !prefs.prefHasUserValue(Sanitizer.prefDidShutdown)) {
+    // this is a shutdown or a startup after an unclean exit
+    Sanitizer.sanitize(null) || // sanitize() returns null on full success
+      prefs.setBoolPref(Sanitizer.prefDidShutdown, true);
+  }
+};
+
+
new file mode 100644
--- /dev/null
+++ b/mobile/chrome/content/sanitize.xul
@@ -0,0 +1,201 @@
+<?xml version="1.0"?>
+
+# -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is the Firefox Sanitizer.
+#
+# The Initial Developer of the Original Code is
+# Ben Goodger.
+# Portions created by the Initial Developer are Copyright (C) 2005
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#   Ben Goodger <ben@mozilla.org>
+#   Giorgio Maone <g.maone@informaction.com>
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+<?xml-stylesheet href="chrome://global/skin/"?>
+
+<!DOCTYPE prefwindow [
+  <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
+  <!ENTITY % sanitizeDTD SYSTEM "chrome://browser/locale/sanitize.dtd">
+  %brandDTD;
+  %sanitizeDTD;
+]>
+
+<prefwindow id="SanitizeDialog" type="child"
+            xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+            dlgbuttons="accept,cancel"
+            title="&sanitizeDialog.title;"
+            style="width: &window.width;;"
+            ondialogaccept="gSanitizePromptDialog.sanitize();">
+
+  <prefpane id="SanitizeDialogPane" onpaneload="gSanitizePromptDialog.init();">
+    <stringbundle id="bundleBrowser" src="chrome://browser/locale/browser.properties"/>
+    
+    <script type="application/x-javascript" src="chrome://browser/content/sanitize.js"/>
+    <script type="application/x-javascript">
+    <![CDATA[
+      var gSanitizePromptDialog = {
+        init: function ()
+        {
+          var s = new Sanitizer();
+          var sanitizePreferences = document.getElementById("sanitizePreferences");
+          for (var i = 0; i < sanitizePreferences.childNodes.length; ++i) {
+            var preference = sanitizePreferences.childNodes[i];
+            var name = s.getNameFromPreference(preference.name);
+            if (!s.canClearItem(name)) 
+              preference.disabled = true;
+          }
+          
+          var bundleBrowser = document.getElementById("bundleBrowser");
+          document.documentElement.getButton("accept").label = bundleBrowser.getString("sanitizeButton");
+        },
+      
+        sanitize: function ()
+        {
+          var s = new Sanitizer();
+          var sanitizePreferences = document.getElementById("sanitizePreferences");
+          var preference, name;
+          for (var i = 0; i < sanitizePreferences.childNodes.length; ++i) {
+            preference = sanitizePreferences.childNodes[i];
+            if (preference.value) {
+              name = s.getNameFromPreference(preference.name);
+              try {
+                s.clearItem(name);
+              } catch(er) {
+                dump(er + " sanitizing " + name); 
+                // TODO: give user feedback about partially failed sanitization
+              }
+            }
+          }
+        },
+        
+        onReadGeneric: function ()
+        {
+          var preferences = document.getElementById("sanitizePreferences");
+          var found = false;
+          for (var i = 0; i < preferences.childNodes.length; ++i) {
+            var preference = preferences.childNodes[i];
+            if (preference.value && !preference.disabled) {
+              found = true;
+              break;
+            }
+          }
+          try {
+            document.documentElement.getButton("accept").disabled = !found;
+          }
+          catch (e) { }
+          return undefined;
+        },
+
+        onReadDownloads: function (aEvent)
+        {
+          // Call the common function that will update the accept button if needed
+          this.onReadGeneric();
+
+          let historyPref = document.getElementById("privacy.item.history")
+          let downloadPref = document.getElementById("privacy.item.downloads");
+
+          // Disable the checkbox if history is selected
+          let downloads = document.getElementById("downloads-checkbox");
+          downloads.disabled = historyPref.value;
+
+          // The "Download History" checkbox is selected if either of the history or
+          // downloads preferences are true.
+          return historyPref.value || downloadPref.value;
+        },
+
+        updateDownloadHistory: function ()
+        {
+          // When toggling history, we automatically clear download history too,
+          // so we disable that control and set its value to true.
+          let downloads = document.getElementById("downloads-checkbox");
+          let history = document.getElementById("history-checkbox");
+          let s = new Sanitizer();
+          downloads.disabled = history.checked ||
+                               !s.canClearItem("downloads");
+          if (history.checked)
+            downloads.checked = true;
+        },
+      };
+    ]]>
+    </script>
+
+    <preferences id="sanitizePreferences">
+      <preference id="privacy.item.history"               name="privacy.item.history"               type="bool" readonly="true"/>
+      <preference id="privacy.item.formdata"              name="privacy.item.formdata"              type="bool" readonly="true"/>
+      <preference id="privacy.item.passwords"             name="privacy.item.passwords"             type="bool" readonly="true"/>
+      <preference id="privacy.item.downloads"             name="privacy.item.downloads"             type="bool" readonly="true"/>
+      <preference id="privacy.item.cookies"               name="privacy.item.cookies"               type="bool" readonly="true"/>
+      <preference id="privacy.item.cache"                 name="privacy.item.cache"                 type="bool" readonly="true"/>
+      <preference id="privacy.item.offlineApps"           name="privacy.item.offlineApps"           type="bool"/>
+      <preference id="privacy.item.sessions"              name="privacy.item.sessions"              type="bool" readonly="true"/>
+    </preferences>
+
+    <description>&sanitizeItems.label;</description>
+
+    <checkbox id="history-checkbox"
+              label="&itemHistory.label;"
+              accesskey="&itemHistory.accesskey;"
+              preference="privacy.item.history"
+              oncommand="gSanitizePromptDialog.updateDownloadHistory();"
+              onsyncfrompreference="return gSanitizePromptDialog.onReadGeneric();"/>
+    <checkbox id="downloads-checkbox"
+              class="indent"
+              label="&itemDownloads.label;"
+              accesskey="&itemDownloads.accesskey;"
+              preference="privacy.item.downloads"
+              onsyncfrompreference="return gSanitizePromptDialog.onReadDownloads();"/>
+    <checkbox label="&itemFormSearchHistory.label;"
+              accesskey="&itemFormSearchHistory.accesskey;"
+              preference="privacy.item.formdata"
+              onsyncfrompreference="return gSanitizePromptDialog.onReadGeneric();"/>
+    <checkbox label="&itemCache.label;"
+              accesskey="&itemCache.accesskey;"
+              preference="privacy.item.cache"
+              onsyncfrompreference="return gSanitizePromptDialog.onReadGeneric();"/>
+    <checkbox label="&itemCookies.label;"
+              accesskey="&itemCookies.accesskey;"
+              preference="privacy.item.cookies"
+              onsyncfrompreference="return gSanitizePromptDialog.onReadGeneric();"/>
+    <checkbox label="&itemOfflineApps.label;"
+              accesskey="&itemOfflineApps.accesskey;"
+              preference="privacy.item.offlineApps"
+              onsyncfrompreference="return gSanitizePromptDialog.onReadGeneric();"/>
+    <checkbox label="&itemPasswords.label;"
+              accesskey="&itemPasswords.accesskey;"
+              preference="privacy.item.passwords"
+              onsyncfrompreference="return gSanitizePromptDialog.onReadGeneric();"/>
+    <checkbox label="&itemSessions.label;"
+              accesskey="&itemSessions.accesskey;"
+              preference="privacy.item.sessions"
+              onsyncfrompreference="return gSanitizePromptDialog.onReadGeneric();"/>
+
+  </prefpane>
+</prefwindow>
--- a/mobile/chrome/jar.mn
+++ b/mobile/chrome/jar.mn
@@ -11,16 +11,19 @@ browser.jar:
   content/browser.css                  (content/browser.css)
   content/scrollbars.css               (content/scrollbars.css)
   content/content.css                  (content/content.css)
 % content branding %branding/
 % locale branding @AB_CD@ %branding/
   branding/brand.dtd                   (locale/@AB_CD@/brand/brand.dtd)
   branding/brand.properties            (locale/@AB_CD@/brand/brand.properties)
 % style chrome://mozapps/content/extensions/extensions.xul chrome://browser/skin/extensions.css
+  content/preferences/richpref.xml
+* content/sanitize.xul
+* content/sanitize.js
 
 classic.jar:
 % skin browser classic/1.0 %
   browser.css                          (skin/browser.css)
   extensions.css                       (skin/extensions.css)
   images/close.png                     (skin/images/close.png)
   images/close-small.png               (skin/images/close-small.png)
   images/default-favicon.png           (skin/images/default-favicon.png)
@@ -28,15 +31,18 @@ classic.jar:
   images/starred48.png                 (skin/images/starred48.png)
   images/page-starred.png              (skin/images/page-starred.png)
   images/tag.png                       (skin/images/tag.png)
   images/throbber.png                  (skin/images/throbber.png)
   images/throbber.gif                  (skin/images/throbber.gif)
   images/toolbar.png                   (skin/images/toolbar.png)
   images/mono-toolbar.png              (skin/images/mono-toolbar.png)
   images/toolbar-background.png        (skin/images/toolbar-background.png)
+  section.css                          (skin/section.css)
+  richpref.css                         (skin/richpref.css)
 
 @AB_CD@.jar:
 % locale browser @AB_CD@ %
   browser.dtd                          (locale/@AB_CD@/browser.dtd)
   browser.properties                   (locale/@AB_CD@/browser.properties)
   search.properties                    (locale/@AB_CD@/search.properties)
   region.properties                    (locale/@AB_CD@/region.properties)
+  preferences.dtd                      (locale/@AB_CD@/preferences.dtd)
new file mode 100644
--- /dev/null
+++ b/mobile/chrome/locale/en-US/preferences.dtd
@@ -0,0 +1,13 @@
+<!ENTITY content.title                             "Content">
+<!ENTITY permissions.default.image.title           "Load images">
+<!ENTITY permissions.default.image.description     "Makes websites pretty">
+<!ENTITY javascript.enabled.title                  "Enable Javascript">
+<!ENTITY javascript.enabled.description            "Makes websites flashy">
+<!ENTITY plugins.enabled.title                     "Enable Plugins">
+<!ENTITY plugins.enabled.description               "Makes websites annoying">
+<!ENTITY privacy.title                             "Privacy">
+<!ENTITY network.cookie.cookieBehavior.title       "Save cookies">
+<!ENTITY network.cookie.cookieBehavior.description "Delicious delicacies">
+<!ENTITY clear.private.data.title                  "Clear private data">
+<!ENTITY clear.private.data.button                 "Clear…">
+<!ENTITY clear.private.data.description            "Saves your hide">
--- a/mobile/chrome/skin/browser.css
+++ b/mobile/chrome/skin/browser.css
@@ -446,8 +446,12 @@ findbar {
   color: MenuText;
 }
 
 #identity-popup-container {
   background-image: none;
   min-width: 280px;
   padding: 10px;
 }
+
+#pref-pane {
+  background-color: rgba(123,125,123,0.9);
+}
new file mode 100644
--- /dev/null
+++ b/mobile/chrome/skin/richpref.css
@@ -0,0 +1,20 @@
+richlistitem.section {
+  font-size: 1.5em ! important;
+  color: white;
+  background-color: grey;
+}
+
+.prefbox {
+  padding: .3em .3em .3em .5em;
+  border: thin solid lightgrey;
+}
+
+.preftitle {
+ font-size: 1.2em ! important;
+}
+
+.prefdesc {
+  font-size: 1em ! important;
+  color: grey;
+  background-color: white;
+}
new file mode 100644
--- /dev/null
+++ b/mobile/chrome/skin/section.css
@@ -0,0 +1,22 @@
+section .section-head {
+  font-size: larger;
+  font-weight: bolder;
+  display: list-item;
+}
+
+section .section-indicator {
+  width: 16px;
+  height: 16px;
+}
+
+section image.closed {
+  list-style-image: url("chrome://global/skin/arrow/arrow-rit-sharp.gif");
+}
+
+section image.open {
+  list-style-image: url("chrome://global/skin/arrow/arrow-dn-sharp.gif");
+}
+
+section > vbox > vbox {
+  margin-left: 16px;
+}