Bug 774963 - Remove removeDataFromDomain from nsIPrivateBrowsingService, and put it in its own JS module, ClearRecentHistory; r=jdm
authorEhsan Akhgari <ehsan@mozilla.com>
Wed, 03 Oct 2012 11:29:10 -0400
changeset 115457 41d840c9cf301290affa11fe7bcc67711dfbeb84
parent 115439 09c4dd7bf7d6273f85db1e5b11a1e064e4650aaf
child 115458 94ab602328ff6a2ab9b2daf75125695038ad57db
push id1708
push userakeybl@mozilla.com
push dateMon, 19 Nov 2012 21:10:21 +0000
treeherdermozilla-beta@27b14fe50103 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjdm
bugs774963
milestone18.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 774963 - Remove removeDataFromDomain from nsIPrivateBrowsingService, and put it in its own JS module, ClearRecentHistory; r=jdm
browser/components/places/content/controller.js
browser/components/preferences/aboutPermissions.js
browser/components/preferences/tests/browser_chunk_permissions.js
browser/components/privatebrowsing/src/ClearRecentHistory.jsm
browser/components/privatebrowsing/src/Makefile.in
browser/components/privatebrowsing/src/nsPrivateBrowsingService.js
browser/components/privatebrowsing/src/nsPrivateBrowsingServiceWrapper.cpp
browser/components/privatebrowsing/test/browser/browser_privatebrowsing_clearplugindata.js
browser/components/privatebrowsing/test/unit/do_test_removeDataFromDomain.js
browser/components/privatebrowsing/test/unit/do_test_removeDataFromDomain_activeDownloads.js
browser/components/privatebrowsing/test/unit/test_privatebrowsingwrapper_removeDataFromDomain.js
browser/components/privatebrowsing/test/unit/test_privatebrowsingwrapper_removeDataFromDomain_activeDownloads.js
browser/components/privatebrowsing/test/unit/test_removeDataFromDomain.js
browser/components/privatebrowsing/test/unit/test_removeDataFromDomain_activeDownloads.js
browser/components/privatebrowsing/test/unit/xpcshell.ini
browser/components/sessionstore/test/browser_394759_purge.js
browser/components/sessionstore/test/browser_464199.js
dom/indexedDB/test/browser_forgetThisSite.js
netwerk/base/public/nsIPrivateBrowsingService.idl
--- a/browser/components/places/content/controller.js
+++ b/browser/components/places/content/controller.js
@@ -1,14 +1,15 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+Components.utils.import("resource://gre/modules/ClearRecentHistory.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
                                   "resource://gre/modules/NetUtil.jsm");
 
 // XXXmano: we should move most/all of these constants to PlacesUtils
 const ORGANIZER_ROOT_BOOKMARKS = "place:folder=BOOKMARKS_MENU&excludeItems=1&queryType=1";
 
 // No change to the view, preserve current selection
 const RELOAD_ACTION_NOTHING = 0;
@@ -221,17 +222,17 @@ PlacesController.prototype = {
     case "placesCmd_deleteDataHost":
       var host;
       if (PlacesUtils.nodeIsHost(this._view.selectedNode)) {
         var queries = this._view.selectedNode.getQueries();
         host = queries[0].domain;
       }
       else
         host = NetUtil.newURI(this._view.selectedNode.uri).host;
-      PlacesUIUtils.privateBrowsing.removeDataFromDomain(host);
+      ClearRecentHistory.removeDataFromDomain(host);
       break;
     case "cmd_selectAll":
       this.selectAll();
       break;
     case "placesCmd_open":
       PlacesUIUtils.openNodeIn(this._view.selectedNode, "current", this._view);
       break;
     case "placesCmd_open:window":
--- a/browser/components/preferences/aboutPermissions.js
+++ b/browser/components/preferences/aboutPermissions.js
@@ -5,16 +5,17 @@
 let Ci = Components.interfaces;
 let Cc = Components.classes;
 let Cu = Components.utils;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/PluralForm.jsm");
 Cu.import("resource://gre/modules/DownloadUtils.jsm");
 Cu.import("resource://gre/modules/NetUtil.jsm");
+Cu.import("resource://gre/modules/ClearRecentHistory.jsm");
 
 let gFaviconService = Cc["@mozilla.org/browser/favicon-service;1"].
                       getService(Ci.nsIFaviconService);
 
 let gPlacesDatabase = Cc["@mozilla.org/browser/nav-history-service;1"].
                       getService(Ci.nsPIPlacesDatabase).
                       DBConnection.
                       clone(true);
@@ -226,19 +227,17 @@ Site.prototype = {
     Services.logins.setLoginSavingEnabled(this.httpURI.prePath, isEnabled);
     Services.logins.setLoginSavingEnabled(this.httpsURI.prePath, isEnabled);
   },
 
   /**
    * Removes all data from the browser corresponding to the site.
    */
   forgetSite: function Site_forgetSite() {
-    let pb = Cc["@mozilla.org/privatebrowsing;1"].
-             getService(Ci.nsIPrivateBrowsingService);
-    pb.removeDataFromDomain(this.host);
+    ClearRecentHistory.removeDataFromDomain(this.host);
   }
 }
 
 /**
  * PermissionDefaults object keeps track of default permissions for sites based
  * on global preferences.
  *
  * Inspired by pageinfo/permissions.js
--- a/browser/components/preferences/tests/browser_chunk_permissions.js
+++ b/browser/components/preferences/tests/browser_chunk_permissions.js
@@ -1,13 +1,14 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 Components.utils.import("resource://gre/modules/PlacesUtils.jsm");
 Components.utils.import("resource://gre/modules/NetUtil.jsm");
+Components.utils.import("resource://gre/modules/ClearRecentHistory.jsm");
 
 const ABOUT_PERMISSIONS_SPEC = "about:permissions";
 
 const TEST_URI_1 = NetUtil.newURI("http://mozilla.com/");
 const TEST_URI_2 = NetUtil.newURI("http://mozilla.org/");
 const TEST_URI_3 = NetUtil.newURI("http://wikipedia.org/");
 
 // values from DefaultPermissions object
@@ -110,19 +111,17 @@ var tests = [
       ok(testSite3.collapsed, "test site 3 is collapsed after early filtering");
 
       runNextTest();
     }
   },
   {
     desc: "test removing from sites-list before it is fully constructed.",
     preInit: function() {
-      let pb = Cc["@mozilla.org/privatebrowsing;1"].
-                 getService(Ci.nsIPrivateBrowsingService);
-      pb.removeDataFromDomain(TEST_URI_2.host);
+      ClearRecentHistory.removeDataFromDomain(TEST_URI_2.host);
     },
     run: function() {
       let testSite1 = getSiteItem(TEST_URI_1.host);
       ok(!testSite2, "test site 1 was not removed from sites list");
       let testSite2 = getSiteItem(TEST_URI_2.host);
       ok(!testSite2, "test site 2 was pre-removed from sites list");
       let testSite3 = getSiteItem(TEST_URI_3.host);
       ok(!testSite2, "test site 3 was not removed from sites list");
new file mode 100644
--- /dev/null
+++ b/browser/components/privatebrowsing/src/ClearRecentHistory.jsm
@@ -0,0 +1,226 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+var EXPORTED_SYMBOLS = ["ClearRecentHistory"];
+
+/**
+ * Returns true if the string passed in is part of the root domain of the
+ * current string.  For example, if this is "www.mozilla.org", and we pass in
+ * "mozilla.org", this will return true.  It would return false the other way
+ * around.
+ */
+function hasRootDomain(str, aDomain)
+{
+  let index = str.indexOf(aDomain);
+  // If aDomain is not found, we know we do not have it as a root domain.
+  if (index == -1)
+    return false;
+
+  // If the strings are the same, we obviously have a match.
+  if (str == aDomain)
+    return true;
+
+  // Otherwise, we have aDomain as our root domain iff the index of aDomain is
+  // aDomain.length subtracted from our length and (since we do not have an
+  // exact match) the character before the index is a dot or slash.
+  let prevChar = str[index - 1];
+  return (index == (str.length - aDomain.length)) &&
+         (prevChar == "." || prevChar == "/");
+}
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+
+var ClearRecentHistory = {
+  removeDataFromDomain: function CRH_removeDataFromDomain(aDomain)
+  {
+    // clear any and all network geolocation provider sessions
+    try {
+        Services.prefs.deleteBranch("geo.wifi.access_token.");
+    } catch (e) {}
+
+    // History
+    let (bh = Cc["@mozilla.org/browser/global-history;2"].
+              getService(Ci.nsIBrowserHistory)) {
+      bh.removePagesFromHost(aDomain, true);
+    }
+
+    // Cache
+    let (cs = Cc["@mozilla.org/network/cache-service;1"].
+              getService(Ci.nsICacheService)) {
+      // NOTE: there is no way to clear just that domain, so we clear out
+      //       everything)
+      try {
+        cs.evictEntries(Ci.nsICache.STORE_ANYWHERE);
+      } catch (ex) {
+        Cu.reportError("Exception thrown while clearing the cache: " +
+          ex.toString());
+      }
+    }
+
+    // Image Cache
+    let (imageCache = Cc["@mozilla.org/image/tools;1"].
+                      getService(Ci.imgITools).getImgCacheForDocument(null)) {
+      try {
+        imageCache.clearCache(false); // true=chrome, false=content
+      } catch (ex) {
+        Cu.reportError("Exception thrown while clearing the image cache: " +
+          ex.toString());
+      }
+    }
+
+    // Cookies
+    let (cm = Cc["@mozilla.org/cookiemanager;1"].
+              getService(Ci.nsICookieManager2)) {
+      let enumerator = cm.getCookiesFromHost(aDomain);
+      while (enumerator.hasMoreElements()) {
+        let cookie = enumerator.getNext().QueryInterface(Ci.nsICookie);
+        cm.remove(cookie.host, cookie.name, cookie.path, false);
+      }
+    }
+
+    // Plugin data
+    const phInterface = Ci.nsIPluginHost;
+    const FLAG_CLEAR_ALL = phInterface.FLAG_CLEAR_ALL;
+    let (ph = Cc["@mozilla.org/plugin/host;1"].getService(phInterface)) {
+      let tags = ph.getPluginTags();
+      for (let i = 0; i < tags.length; i++) {
+        try {
+          ph.clearSiteData(tags[i], aDomain, FLAG_CLEAR_ALL, -1);
+        } catch (e) {
+          // Ignore errors from the plugin
+        }
+      }
+    }
+
+    // Downloads
+    let (dm = Cc["@mozilla.org/download-manager;1"].
+              getService(Ci.nsIDownloadManager)) {
+      // Active downloads
+      let enumerator = dm.activeDownloads;
+      while (enumerator.hasMoreElements()) {
+        let dl = enumerator.getNext().QueryInterface(Ci.nsIDownload);
+        if (hasRootDomain(dl.source.host, aDomain)) {
+          dm.cancelDownload(dl.id);
+          dm.removeDownload(dl.id);
+        }
+      }
+
+      // Completed downloads
+      let db = dm.DBConnection;
+      // NOTE: This is lossy, but we feel that it is OK to be lossy here and not
+      //       invoke the cost of creating a URI for each download entry and
+      //       ensure that the hostname matches.
+      let stmt = db.createStatement(
+        "DELETE FROM moz_downloads " +
+        "WHERE source LIKE ?1 ESCAPE '/' " +
+        "AND state NOT IN (?2, ?3, ?4)"
+      );
+      let pattern = stmt.escapeStringForLIKE(aDomain, "/");
+      stmt.bindByIndex(0, "%" + pattern + "%");
+      stmt.bindByIndex(1, Ci.nsIDownloadManager.DOWNLOAD_DOWNLOADING);
+      stmt.bindByIndex(2, Ci.nsIDownloadManager.DOWNLOAD_PAUSED);
+      stmt.bindByIndex(3, Ci.nsIDownloadManager.DOWNLOAD_QUEUED);
+      try {
+        stmt.execute();
+      }
+      finally {
+        stmt.finalize();
+      }
+
+      // We want to rebuild the list if the UI is showing, so dispatch the
+      // observer topic
+      let os = Cc["@mozilla.org/observer-service;1"].
+               getService(Ci.nsIObserverService);
+      os.notifyObservers(null, "download-manager-remove-download", null);
+    }
+
+    // Passwords
+    let (lm = Cc["@mozilla.org/login-manager;1"].
+              getService(Ci.nsILoginManager)) {
+      // Clear all passwords for domain
+      try {
+        let logins = lm.getAllLogins();
+        for (let i = 0; i < logins.length; i++)
+          if (hasRootDomain(logins[i].hostname, aDomain))
+            lm.removeLogin(logins[i]);
+      }
+      // XXXehsan: is there a better way to do this rather than this
+      // hacky comparison?
+      catch (ex if ex.message.indexOf("User canceled Master Password entry") != -1) { }
+
+      // Clear any "do not save for this site" for this domain
+      let disabledHosts = lm.getAllDisabledHosts();
+      for (let i = 0; i < disabledHosts.length; i++)
+        if (hasRootDomain(disabledHosts[i], aDomain))
+          lm.setLoginSavingEnabled(disabledHosts, true);
+    }
+
+    // Permissions
+    let (pm = Cc["@mozilla.org/permissionmanager;1"].
+              getService(Ci.nsIPermissionManager)) {
+      // Enumerate all of the permissions, and if one matches, remove it
+      let enumerator = pm.enumerator;
+      while (enumerator.hasMoreElements()) {
+        let perm = enumerator.getNext().QueryInterface(Ci.nsIPermission);
+        if (hasRootDomain(perm.host, aDomain))
+          pm.remove(perm.host, perm.type);
+      }
+    }
+
+    // Content Preferences
+    let (cp = Cc["@mozilla.org/content-pref/service;1"].
+              getService(Ci.nsIContentPrefService)) {
+      let db = cp.DBConnection;
+      // First we need to get the list of "groups" which are really just domains
+      let names = [];
+      let stmt = db.createStatement(
+        "SELECT name " +
+        "FROM groups " +
+        "WHERE name LIKE ?1 ESCAPE '/'"
+      );
+      let pattern = stmt.escapeStringForLIKE(aDomain, "/");
+      stmt.bindByIndex(0, "%" + pattern);
+      try {
+        while (stmt.executeStep())
+          if (hasRootDomain(stmt.getString(0), aDomain))
+            names.push(stmt.getString(0));
+      }
+      finally {
+        stmt.finalize();
+      }
+
+      // Now, for each name we got back, remove all of its prefs.
+      for (let i = 0; i < names.length; i++) {
+        let uri = names[i];
+        let enumerator = cp.getPrefs(uri).enumerator;
+        while (enumerator.hasMoreElements()) {
+          let pref = enumerator.getNext().QueryInterface(Ci.nsIProperty);
+          cp.removePref(uri, pref.name);
+        }
+      }
+    }
+
+    // Indexed DB
+    let (idbm = Cc["@mozilla.org/dom/indexeddb/manager;1"].
+                getService(Ci.nsIIndexedDatabaseManager)) {
+      // delete data from both HTTP and HTTPS sites
+      let caUtils = {};
+      let scriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
+                         getService(Ci.mozIJSSubScriptLoader);
+      scriptLoader.loadSubScript("chrome://global/content/contentAreaUtils.js",
+                                 caUtils);
+      let httpURI = caUtils.makeURI("http://" + aDomain);
+      let httpsURI = caUtils.makeURI("https://" + aDomain);
+      idbm.clearDatabasesForURI(httpURI);
+      idbm.clearDatabasesForURI(httpsURI);
+    }
+
+    // Everybody else (including extensions)
+    Services.obs.notifyObservers(null, "browser:purge-domain-data", aDomain);
+  }
+};
--- a/browser/components/privatebrowsing/src/Makefile.in
+++ b/browser/components/privatebrowsing/src/Makefile.in
@@ -24,9 +24,13 @@ LOCAL_INCLUDES = -I$(srcdir)/../../build
 EXTRA_COMPONENTS = \
   nsPrivateBrowsingService.manifest \
   $(NULL)
 
 EXTRA_PP_COMPONENTS = \
 	nsPrivateBrowsingService.js \
 	$(NULL)
 
+EXTRA_JS_MODULES = \
+	ClearRecentHistory.jsm \
+	$(NULL)
+
 include $(topsrcdir)/config/rules.mk
--- a/browser/components/privatebrowsing/src/nsPrivateBrowsingService.js
+++ b/browser/components/privatebrowsing/src/nsPrivateBrowsingService.js
@@ -5,44 +5,16 @@
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
 Components.utils.import("resource://gre/modules/Services.jsm");
 
 #ifndef XP_WIN
 #define BROKEN_WM_Z_ORDER
 #endif
 
 ////////////////////////////////////////////////////////////////////////////////
-//// Utilities
-
-/**
- * Returns true if the string passed in is part of the root domain of the
- * current string.  For example, if this is "www.mozilla.org", and we pass in
- * "mozilla.org", this will return true.  It would return false the other way
- * around.
- */
-String.prototype.hasRootDomain = function hasRootDomain(aDomain)
-{
-  let index = this.indexOf(aDomain);
-  // If aDomain is not found, we know we do not have it as a root domain.
-  if (index == -1)
-    return false;
-
-  // If the strings are the same, we obviously have a match.
-  if (this == aDomain)
-    return true;
-
-  // Otherwise, we have aDomain as our root domain iff the index of aDomain is
-  // aDomain.length subtracted from our length and (since we do not have an
-  // exact match) the character before the index is a dot or slash.
-  let prevChar = this[index - 1];
-  return (index == (this.length - aDomain.length)) &&
-         (prevChar == "." || prevChar == "/");
-}
-
-////////////////////////////////////////////////////////////////////////////////
 //// Constants
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 const Cr = Components.results;
 
 const STATE_IDLE = 0;
@@ -101,17 +73,17 @@ PrivateBrowsingService.prototype = {
 
   // Telemetry measurements
   _enterTimestamps: {},
   _exitTimestamps: {},
 
   // XPCOM registration
   classID: Components.ID("{c31f4883-839b-45f6-82ad-a6a9bc5ad599}"),
 
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPrivateBrowsingService, 
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPrivateBrowsingService,
                                          Ci.nsIObserver,
                                          Ci.nsISupportsWeakReference,
                                          Ci.nsICommandLineHandler]),
 
   _unload: function PBS__destroy() {
     // Force an exit from the private browsing mode on shutdown
     this._quitting = true;
     if (this._inPrivateBrowsing)
@@ -603,201 +575,12 @@ PrivateBrowsingService.prototype = {
     return this._inPrivateBrowsing && this._autoStarted;
   },
 
   /**
    * Whether the latest transition was initiated from the command line.
    */
   get lastChangedByCommandLine() {
     return this._lastChangedByCommandLine;
-  },
-
-  removeDataFromDomain: function PBS_removeDataFromDomain(aDomain)
-  {
-
-    // clear any and all network geolocation provider sessions
-    try {
-        this._prefs.deleteBranch("geo.wifi.access_token.");
-    } catch (e) {}
-    
-    // History
-    let (bh = Cc["@mozilla.org/browser/global-history;2"].
-              getService(Ci.nsIBrowserHistory)) {
-      bh.removePagesFromHost(aDomain, true);
-    }
-
-    // Cache
-    let (cs = Cc["@mozilla.org/network/cache-service;1"].
-              getService(Ci.nsICacheService)) {
-      // NOTE: there is no way to clear just that domain, so we clear out
-      //       everything)
-      try {
-        cs.evictEntries(Ci.nsICache.STORE_ANYWHERE);
-      } catch (ex) {
-        Cu.reportError("Exception thrown while clearing the cache: " +
-          ex.toString());
-      }
-    }
-
-    // Image Cache
-    let (imageCache = Cc["@mozilla.org/image/tools;1"].
-                      getService(Ci.imgITools).getImgCacheForDocument(null)) {
-      try {
-        imageCache.clearCache(false); // true=chrome, false=content
-      } catch (ex) {
-        Cu.reportError("Exception thrown while clearing the image cache: " +
-          ex.toString());
-      }
-    }
-
-    // Cookies
-    let (cm = Cc["@mozilla.org/cookiemanager;1"].
-              getService(Ci.nsICookieManager2)) {
-      let enumerator = cm.getCookiesFromHost(aDomain);
-      while (enumerator.hasMoreElements()) {
-        let cookie = enumerator.getNext().QueryInterface(Ci.nsICookie);
-        cm.remove(cookie.host, cookie.name, cookie.path, false);
-      }
-    }
-
-    // Plugin data
-    const phInterface = Ci.nsIPluginHost;
-    const FLAG_CLEAR_ALL = phInterface.FLAG_CLEAR_ALL;
-    let (ph = Cc["@mozilla.org/plugin/host;1"].getService(phInterface)) {
-      let tags = ph.getPluginTags();
-      for (let i = 0; i < tags.length; i++) {
-        try {
-          ph.clearSiteData(tags[i], aDomain, FLAG_CLEAR_ALL, -1);
-        } catch (e) {
-          // Ignore errors from the plugin
-        }
-      }
-    }
-
-    // Downloads
-    let (dm = Cc["@mozilla.org/download-manager;1"].
-              getService(Ci.nsIDownloadManager)) {
-      // Active downloads
-      let enumerator = dm.activeDownloads;
-      while (enumerator.hasMoreElements()) {
-        let dl = enumerator.getNext().QueryInterface(Ci.nsIDownload);
-        if (dl.source.host.hasRootDomain(aDomain)) {
-          dm.cancelDownload(dl.id);
-          dm.removeDownload(dl.id);
-        }
-      }
-
-      // Completed downloads
-      let db = dm.DBConnection;
-      // NOTE: This is lossy, but we feel that it is OK to be lossy here and not
-      //       invoke the cost of creating a URI for each download entry and
-      //       ensure that the hostname matches.
-      let stmt = db.createStatement(
-        "DELETE FROM moz_downloads " +
-        "WHERE source LIKE ?1 ESCAPE '/' " +
-        "AND state NOT IN (?2, ?3, ?4)"
-      );
-      let pattern = stmt.escapeStringForLIKE(aDomain, "/");
-      stmt.bindByIndex(0, "%" + pattern + "%");
-      stmt.bindByIndex(1, Ci.nsIDownloadManager.DOWNLOAD_DOWNLOADING);
-      stmt.bindByIndex(2, Ci.nsIDownloadManager.DOWNLOAD_PAUSED);
-      stmt.bindByIndex(3, Ci.nsIDownloadManager.DOWNLOAD_QUEUED);
-      try {
-        stmt.execute();
-      }
-      finally {
-        stmt.finalize();
-      }
-
-      // We want to rebuild the list if the UI is showing, so dispatch the
-      // observer topic
-      let os = Cc["@mozilla.org/observer-service;1"].
-               getService(Ci.nsIObserverService);
-      os.notifyObservers(null, "download-manager-remove-download", null);
-    }
-
-    // Passwords
-    let (lm = Cc["@mozilla.org/login-manager;1"].
-              getService(Ci.nsILoginManager)) {
-      // Clear all passwords for domain
-      try {
-        let logins = lm.getAllLogins();
-        for (let i = 0; i < logins.length; i++)
-          if (logins[i].hostname.hasRootDomain(aDomain))
-            lm.removeLogin(logins[i]);
-      }
-      // XXXehsan: is there a better way to do this rather than this
-      // hacky comparison?
-      catch (ex if ex.message.indexOf("User canceled Master Password entry") != -1) { }
-
-      // Clear any "do not save for this site" for this domain
-      let disabledHosts = lm.getAllDisabledHosts();
-      for (let i = 0; i < disabledHosts.length; i++)
-        if (disabledHosts[i].hasRootDomain(aDomain))
-          lm.setLoginSavingEnabled(disabledHosts, true);
-    }
-
-    // Permissions
-    let (pm = Cc["@mozilla.org/permissionmanager;1"].
-              getService(Ci.nsIPermissionManager)) {
-      // Enumerate all of the permissions, and if one matches, remove it
-      let enumerator = pm.enumerator;
-      while (enumerator.hasMoreElements()) {
-        let perm = enumerator.getNext().QueryInterface(Ci.nsIPermission);
-        if (perm.host.hasRootDomain(aDomain))
-          pm.remove(perm.host, perm.type);
-      }
-    }
-
-    // Content Preferences
-    let (cp = Cc["@mozilla.org/content-pref/service;1"].
-              getService(Ci.nsIContentPrefService)) {
-      let db = cp.DBConnection;
-      // First we need to get the list of "groups" which are really just domains
-      let names = [];
-      let stmt = db.createStatement(
-        "SELECT name " +
-        "FROM groups " +
-        "WHERE name LIKE ?1 ESCAPE '/'"
-      );
-      let pattern = stmt.escapeStringForLIKE(aDomain, "/");
-      stmt.bindByIndex(0, "%" + pattern);
-      try {
-        while (stmt.executeStep())
-          if (stmt.getString(0).hasRootDomain(aDomain))
-            names.push(stmt.getString(0));
-      }
-      finally {
-        stmt.finalize();
-      }
-
-      // Now, for each name we got back, remove all of its prefs.
-      for (let i = 0; i < names.length; i++) {
-        let uri = names[i];
-        let enumerator = cp.getPrefs(uri).enumerator;
-        while (enumerator.hasMoreElements()) {
-          let pref = enumerator.getNext().QueryInterface(Ci.nsIProperty);
-          cp.removePref(uri, pref.name);
-        }
-      }
-    }
-
-    // Indexed DB
-    let (idbm = Cc["@mozilla.org/dom/indexeddb/manager;1"].
-                getService(Ci.nsIIndexedDatabaseManager)) {
-      // delete data from both HTTP and HTTPS sites
-      let caUtils = {};
-      let scriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
-                         getService(Ci.mozIJSSubScriptLoader);
-      scriptLoader.loadSubScript("chrome://global/content/contentAreaUtils.js",
-                                 caUtils);
-      let httpURI = caUtils.makeURI("http://" + aDomain);
-      let httpsURI = caUtils.makeURI("https://" + aDomain);
-      idbm.clearDatabasesForURI(httpURI);
-      idbm.clearDatabasesForURI(httpsURI);
-    }
-
-    // Everybody else (including extensions)
-    this._obs.notifyObservers(null, "browser:purge-domain-data", aDomain);
   }
 };
 
 var NSGetFactory = XPCOMUtils.generateNSGetFactory([PrivateBrowsingService]);
--- a/browser/components/privatebrowsing/src/nsPrivateBrowsingServiceWrapper.cpp
+++ b/browser/components/privatebrowsing/src/nsPrivateBrowsingServiceWrapper.cpp
@@ -79,22 +79,15 @@ nsPrivateBrowsingServiceWrapper::GetLast
 {
   if (!aReason)
     return NS_ERROR_NULL_POINTER;
   JSStackGuard guard;
   return mPBService->GetLastChangedByCommandLine(aReason);
 }
 
 NS_IMETHODIMP
-nsPrivateBrowsingServiceWrapper::RemoveDataFromDomain(const nsACString & aDomain)
-{
-  JSStackGuard guard;
-  return mPBService->RemoveDataFromDomain(aDomain);
-}
-
-NS_IMETHODIMP
 nsPrivateBrowsingServiceWrapper::Observe(nsISupports *aSubject, const char *aTopic, const PRUnichar *aData)
 {
   JSStackGuard guard;
   nsCOMPtr<nsIObserver> observer(do_QueryInterface(mPBService));
   NS_ENSURE_TRUE(observer, NS_ERROR_FAILURE);
   return observer->Observe(aSubject, aTopic, aData);
 }
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_clearplugindata.js
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_clearplugindata.js
@@ -1,13 +1,15 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
+Components.utils.import("resource://gre/modules/ClearRecentHistory.jsm");
+
 // Test clearing plugin data by domain using nsPrivateBrowsingService.
 const testURL = "http://mochi.test:8888/browser/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_clearplugindata.html";
 
 const pluginHostIface = Ci.nsIPluginHost;
 var pluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
 pluginHost.QueryInterface(pluginHostIface);
 
 var pluginTag;
@@ -61,35 +63,32 @@ function do_test()
   gBrowser.selectedTab = gBrowser.addTab();
   gBrowser.selectedBrowser.addEventListener("load", function () {
     gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
 
     setFinishedCallback(function() {
       ok(stored(["192.168.1.1","foo.com","nonexistent.foo.com","bar.com","localhost"]),
         "Data stored for sites");
 
-      let pb = Cc["@mozilla.org/privatebrowsing;1"].
-               getService(Ci.nsIPrivateBrowsingService);
-
       // Clear data for "foo.com" and its subdomains.
-      pb.removeDataFromDomain("foo.com");
+      ClearRecentHistory.removeDataFromDomain("foo.com");
       ok(stored(["bar.com","192.168.1.1","localhost"]), "Data stored for sites");
       ok(!stored(["foo.com"]), "Data cleared for foo.com");
       ok(!stored(["bar.foo.com"]), "Data cleared for subdomains of foo.com");
 
       // Clear data for "bar.com" using a subdomain.
-      pb.removeDataFromDomain("foo.bar.com");
+      ClearRecentHistory.removeDataFromDomain("foo.bar.com");
       ok(!stored(["bar.com"]), "Data cleared for bar.com");
 
       // Clear data for "192.168.1.1".
-      pb.removeDataFromDomain("192.168.1.1");
+      ClearRecentHistory.removeDataFromDomain("192.168.1.1");
       ok(!stored(["192.168.1.1"]), "Data cleared for 192.168.1.1");
 
       // Clear data for "localhost".
-      pb.removeDataFromDomain("localhost");
+      ClearRecentHistory.removeDataFromDomain("localhost");
       ok(!stored(null), "All data cleared");
 
       gBrowser.removeCurrentTab();
 
       executeSoon(finish);
     });
   }, true);
   content.location = testURL;
deleted file mode 100644
--- a/browser/components/privatebrowsing/test/unit/do_test_removeDataFromDomain.js
+++ /dev/null
@@ -1,630 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- * vim: sw=2 ts=2 sts=2
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-/**
- * Test added with bug 460086 to test the behavior of the new API that was added
- * to remove all traces of visiting a site.
- */
-
-////////////////////////////////////////////////////////////////////////////////
-//// Globals
-
-Cu.import("resource://gre/modules/PlacesUtils.jsm");
-
-let pb = Cc[PRIVATEBROWSING_CONTRACT_ID].
-         getService(Ci.nsIPrivateBrowsingService);
-
-const COOKIE_EXPIRY = Math.round(Date.now() / 1000) + 60;
-const COOKIE_NAME = "testcookie";
-const COOKIE_PATH = "/";
-
-const LOGIN_USERNAME = "username";
-const LOGIN_PASSWORD = "password";
-const LOGIN_USERNAME_FIELD = "username_field";
-const LOGIN_PASSWORD_FIELD = "password_field";
-
-const PERMISSION_TYPE = "test-perm";
-const PERMISSION_VALUE = Ci.nsIPermissionManager.ALLOW_ACTION;
-
-const PREFERENCE_NAME = "test-pref";
-
-////////////////////////////////////////////////////////////////////////////////
-//// Utility Functions
-
-/**
- * Creates an nsIURI object for the given string representation of a URI.
- *
- * @param aURIString
- *        The spec of the URI to create.
- * @returns an nsIURI representing aURIString.
- */
-function uri(aURIString)
-{
-  return Cc["@mozilla.org/network/io-service;1"].
-         getService(Ci.nsIIOService).
-         newURI(aURIString, null, null);
-}
-
-/**
- * Adds a visit to history.
- *
- * @param aURI
- *        The URI to add.
- */
-function add_visit(aURI)
-{
-  check_visited(aURI, false);
-  PlacesUtils.history.addVisit(aURI, Date.now() * 1000, null,
-                               Ci.nsINavHistoryService.TRANSITION_LINK, false,
-                               0);
-  check_visited(aURI, true);
-}
-
-/**
- * Checks to ensure a URI string is visited or not.
- *
- * @param aURI
- *        The URI to check.
- * @param aIsVisited
- *        True if the URI should be visited, false otherwise.
- */
-function check_visited(aURI, aIsVisited)
-{
-  let checker = aIsVisited ? do_check_true : do_check_false;
-  checker(PlacesUtils.ghistory2.isVisited(aURI));
-}
-
-/**
- * Add a cookie to the cookie service.
- *
- * @param aDomain
- */
-function add_cookie(aDomain)
-{
-  check_cookie_exists(aDomain, false);
-  let cm = Cc["@mozilla.org/cookiemanager;1"].getService(Ci.nsICookieManager2);
-  cm.add(aDomain, COOKIE_PATH, COOKIE_NAME, "", false, false, false,
-         COOKIE_EXPIRY);
-  check_cookie_exists(aDomain, true);
-}
-
-/**
- * Checks to ensure that a cookie exists or not for a domain.
- *
- * @param aDomain
- *        The domain to check for the cookie.
- * @param aExists
- *        True if the cookie should exist, false otherwise.
- */
-function check_cookie_exists(aDomain, aExists)
-{
-  let cm = Cc["@mozilla.org/cookiemanager;1"].getService(Ci.nsICookieManager2);
-  let cookie = {
-    host: aDomain,
-    name: COOKIE_NAME,
-    path: COOKIE_PATH
-  }
-  let checker = aExists ? do_check_true : do_check_false;
-  checker(cm.cookieExists(cookie));
-}
-
-/**
- * Adds a download to download history.
- *
- * @param aURIString
- *        The string of the URI to add.
- * @param aIsActive
- *        If it should be set to an active state in the database.  This does not
- *        make it show up in the list of active downloads however!
- */
-function add_download(aURIString, aIsActive)
-{
-  check_downloaded(aURIString, false);
-  let db = Cc["@mozilla.org/download-manager;1"].
-           getService(Ci.nsIDownloadManager).
-           DBConnection;
-  let stmt = db.createStatement(
-    "INSERT INTO moz_downloads (source, state) " +
-    "VALUES (:source, :state)"
-  );
-  stmt.params.source = aURIString;
-  stmt.params.state = aIsActive ? Ci.nsIDownloadManager.DOWNLOAD_DOWNLOADING :
-                                  Ci.nsIDownloadManager.DOWNLOAD_FINISHED;
-  try {
-    stmt.execute();
-  }
-  finally {
-    stmt.finalize();
-  }
-  check_downloaded(aURIString, true);
-}
-
-/**
- * Checks to ensure a URI string is in download history or not.
- *
- * @param aURIString
- *        The string of the URI to check.
- * @param aIsDownloaded
- *        True if the URI should be downloaded, false otherwise.
- */
-function check_downloaded(aURIString, aIsDownloaded)
-{
-  let db = Cc["@mozilla.org/download-manager;1"].
-           getService(Ci.nsIDownloadManager).
-           DBConnection;
-  let stmt = db.createStatement(
-    "SELECT * " +
-    "FROM moz_downloads " +
-    "WHERE source = :source"
-  );
-  stmt.params.source = aURIString;
-
-  let checker = aIsDownloaded ? do_check_true : do_check_false;
-  try {
-    checker(stmt.executeStep());
-  }
-  finally {
-    stmt.finalize();
-  }
-}
-
-/**
- * Adds a disabled host to the login manager.
- *
- * @param aHost
- *        The host to add to the list of disabled hosts.
- */
-function add_disabled_host(aHost)
-{
-  check_disabled_host(aHost, false);
-  let lm = Cc["@mozilla.org/login-manager;1"].
-           getService(Ci.nsILoginManager);
-  lm.setLoginSavingEnabled(aHost, false);
-  check_disabled_host(aHost, true);
-}
-
-/**
- * Checks to see if a host is disabled for storing logins or not.
- *
- * @param aHost
- *        The host to check if it is disabled.
- * @param aIsDisabled
- *        True if the host should be disabled, false otherwise.
- */
-function check_disabled_host(aHost, aIsDisabled)
-{
-  let lm = Cc["@mozilla.org/login-manager;1"].
-           getService(Ci.nsILoginManager);
-  let checker = aIsDisabled ? do_check_false : do_check_true;
-  checker(lm.getLoginSavingEnabled(aHost));
-}
-
-/**
- * Adds a login for the specified host to the login manager.
- *
- * @param aHost
- *        The host to add the login for.
- */
-function add_login(aHost)
-{
-  check_login_exists(aHost, false);
-  let login = Cc["@mozilla.org/login-manager/loginInfo;1"].
-              createInstance(Ci.nsILoginInfo);
-  login.init(aHost, "", null, LOGIN_USERNAME, LOGIN_PASSWORD,
-             LOGIN_USERNAME_FIELD, LOGIN_PASSWORD_FIELD);
-  let lm = Cc["@mozilla.org/login-manager;1"].
-           getService(Ci.nsILoginManager);
-  lm.addLogin(login);
-  check_login_exists(aHost, true);
-}
-
-/**
- * Checks to see if a login exists for a host.
- *
- * @param aHost
- *        The host to check for the test login.
- * @param aExists
- *        True if the login should exist, false otherwise.
- */
-function check_login_exists(aHost, aExists)
-{
-  let lm = Cc["@mozilla.org/login-manager;1"].
-           getService(Ci.nsILoginManager);
-  let count = { value: 0 };
-  lm.findLogins(count, aHost, "", null);
-  do_check_eq(count.value, aExists ? 1 : 0);
-}
-
-/**
- * Adds a permission for the specified URI to the permission manager.
- *
- * @param aURI
- *        The URI to add the test permission for.
- */
-function add_permission(aURI)
-{
-  check_permission_exists(aURI, false);
-  let pm = Cc["@mozilla.org/permissionmanager;1"].
-           getService(Ci.nsIPermissionManager);
-  let principal = Cc["@mozilla.org/scriptsecuritymanager;1"]
-                    .getService(Ci.nsIScriptSecurityManager)
-                    .getNoAppCodebasePrincipal(aURI);
-
-  pm.addFromPrincipal(principal, PERMISSION_TYPE, PERMISSION_VALUE);
-  check_permission_exists(aURI, true);
-}
-
-/**
- * Checks to see if a permission exists for the given URI.
- *
- * @param aURI
- *        The URI to check if a permission exists.
- * @param aExists
- *        True if the permission should exist, false otherwise.
- */
-function check_permission_exists(aURI, aExists)
-{
-  let pm = Cc["@mozilla.org/permissionmanager;1"].
-           getService(Ci.nsIPermissionManager);
-  let principal = Cc["@mozilla.org/scriptsecuritymanager;1"]
-                    .getService(Ci.nsIScriptSecurityManager)
-                    .getNoAppCodebasePrincipal(aURI);
-
-  let perm = pm.testExactPermissionFromPrincipal(principal, PERMISSION_TYPE);
-  let checker = aExists ? do_check_eq : do_check_neq;
-  checker(perm, PERMISSION_VALUE);
-}
-
-/**
- * Adds a content preference for the specified URI.
- *
- * @param aURI
- *        The URI to add a preference for.
- */
-function add_preference(aURI)
-{
-  check_preference_exists(aURI, false);
-  let cp = Cc["@mozilla.org/content-pref/service;1"].
-           getService(Ci.nsIContentPrefService);
-  cp.setPref(aURI, PREFERENCE_NAME, "foo");
-  check_preference_exists(aURI, true);
-}
-
-/**
- * Checks to see if a preference exists for the given URI.
- *
- * @param aURI
- *        The URI to check if a preference exists.
- * @param aExists
- *        True if the permission should exist, false otherwise.
- */
-function check_preference_exists(aURI, aExists)
-{
-  let cp = Cc["@mozilla.org/content-pref/service;1"].
-           getService(Ci.nsIContentPrefService);
-  let checker = aExists ? do_check_true : do_check_false;
-  checker(cp.hasPref(aURI, PREFERENCE_NAME));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-//// Test Functions
-
-// History
-function test_history_cleared_with_direct_match()
-{
-  const TEST_URI = uri("http://mozilla.org/foo");
-  add_visit(TEST_URI);
-  pb.removeDataFromDomain("mozilla.org");
-  check_visited(TEST_URI, false);
-}
-
-function test_history_cleared_with_subdomain()
-{
-  const TEST_URI = uri("http://www.mozilla.org/foo");
-  add_visit(TEST_URI);
-  pb.removeDataFromDomain("mozilla.org");
-  check_visited(TEST_URI, false);
-}
-
-function test_history_not_cleared_with_uri_contains_domain()
-{
-  const TEST_URI = uri("http://ilovemozilla.org/foo");
-  add_visit(TEST_URI);
-  pb.removeDataFromDomain("mozilla.org");
-  check_visited(TEST_URI, true);
-
-  // Clear history since we left something there from this test.
-  PlacesUtils.bhistory.removeAllPages();
-}
-
-// Cookie Service
-function test_cookie_cleared_with_direct_match()
-{
-  const TEST_DOMAIN = "mozilla.org";
-  add_cookie(TEST_DOMAIN);
-  pb.removeDataFromDomain("mozilla.org");
-  check_cookie_exists(TEST_DOMAIN, false);
-}
-
-function test_cookie_cleared_with_subdomain()
-{
-  const TEST_DOMAIN = "www.mozilla.org";
-  add_cookie(TEST_DOMAIN);
-  pb.removeDataFromDomain("mozilla.org");
-  check_cookie_exists(TEST_DOMAIN, false);
-}
-
-function test_cookie_not_cleared_with_uri_contains_domain()
-{
-  const TEST_DOMAIN = "ilovemozilla.org";
-  add_cookie(TEST_DOMAIN);
-  pb.removeDataFromDomain("mozilla.org");
-  check_cookie_exists(TEST_DOMAIN, true);
-}
-
-// Download Manager
-function test_download_history_cleared_with_direct_match()
-{
-  const TEST_URI = "http://mozilla.org/foo";
-  add_download(TEST_URI, false);
-  pb.removeDataFromDomain("mozilla.org");
-  check_downloaded(TEST_URI, false);
-}
-
-function test_download_history_cleared_with_subdomain()
-{
-  const TEST_URI = "http://www.mozilla.org/foo";
-  add_download(TEST_URI, false);
-  pb.removeDataFromDomain("mozilla.org");
-  check_downloaded(TEST_URI, false);
-}
-
-function test_download_history_not_cleared_with_active_direct_match()
-{
-  // Tests that downloads marked as active in the db are not deleted from the db
-  const TEST_URI = "http://mozilla.org/foo";
-  add_download(TEST_URI, true);
-  pb.removeDataFromDomain("mozilla.org");
-  check_downloaded(TEST_URI, true);
-
-  // Reset state
-  let db = Cc["@mozilla.org/download-manager;1"].
-           getService(Ci.nsIDownloadManager).
-           DBConnection;
-  db.executeSimpleSQL("DELETE FROM moz_downloads");
-  check_downloaded(TEST_URI, false);
-}
-
-// Login Manager
-function test_login_manager_disabled_hosts_cleared_with_direct_match()
-{
-  const TEST_HOST = "http://mozilla.org";
-  add_disabled_host(TEST_HOST);
-  pb.removeDataFromDomain("mozilla.org");
-  check_disabled_host(TEST_HOST, false);
-}
-
-function test_login_manager_disabled_hosts_cleared_with_subdomain()
-{
-  const TEST_HOST = "http://www.mozilla.org";
-  add_disabled_host(TEST_HOST);
-  pb.removeDataFromDomain("mozilla.org");
-  check_disabled_host(TEST_HOST, false);
-}
-
-function test_login_manager_disabled_hosts_not_cleared_with_uri_contains_domain()
-{
-  const TEST_HOST = "http://ilovemozilla.org";
-  add_disabled_host(TEST_HOST);
-  pb.removeDataFromDomain("mozilla.org");
-  check_disabled_host(TEST_HOST, true);
-
-  // Reset state
-  let lm = Cc["@mozilla.org/login-manager;1"].
-           getService(Ci.nsILoginManager);
-  lm.setLoginSavingEnabled(TEST_HOST, true);
-  check_disabled_host(TEST_HOST, false);
-}
-
-function test_login_manager_logins_cleared_with_direct_match()
-{
-  const TEST_HOST = "http://mozilla.org";
-  add_login(TEST_HOST);
-  pb.removeDataFromDomain("mozilla.org");
-  check_login_exists(TEST_HOST, false);
-}
-
-function test_login_manager_logins_cleared_with_subdomain()
-{
-  const TEST_HOST = "http://www.mozilla.org";
-  add_login(TEST_HOST);
-  pb.removeDataFromDomain("mozilla.org");
-  check_login_exists(TEST_HOST, false);
-}
-
-function tets_login_manager_logins_not_cleared_with_uri_contains_domain()
-{
-  const TEST_HOST = "http://ilovemozilla.org";
-  add_login(TEST_HOST);
-  pb.removeDataFromDomain("mozilla.org");
-  check_login_exists(TEST_HOST, true);
-
-  let lm = Cc["@mozilla.org/login-manager;1"].
-           getService(Ci.nsILoginManager);
-  lm.removeAllLogins();
-  check_login_exists(TEST_HOST, false);
-}
-
-// Permission Manager
-function test_permission_manager_cleared_with_direct_match()
-{
-  const TEST_URI = uri("http://mozilla.org");
-  add_permission(TEST_URI);
-  pb.removeDataFromDomain("mozilla.org");
-  check_permission_exists(TEST_URI, false);
-}
-
-function test_permission_manager_cleared_with_subdomain()
-{
-  const TEST_URI = uri("http://www.mozilla.org");
-  add_permission(TEST_URI);
-  pb.removeDataFromDomain("mozilla.org");
-  check_permission_exists(TEST_URI, false);
-}
-
-function test_permission_manager_not_cleared_with_uri_contains_domain()
-{
-  const TEST_URI = uri("http://ilovemozilla.org");
-  add_permission(TEST_URI);
-  pb.removeDataFromDomain("mozilla.org");
-  check_permission_exists(TEST_URI, true);
-
-  // Reset state
-  let pm = Cc["@mozilla.org/permissionmanager;1"].
-           getService(Ci.nsIPermissionManager);
-  pm.removeAll();
-  check_permission_exists(TEST_URI, false);
-}
-
-// Content Preferences
-function test_content_preferences_cleared_with_direct_match()
-{
-  const TEST_URI = uri("http://mozilla.org");
-  add_preference(TEST_URI);
-  pb.removeDataFromDomain("mozilla.org");
-  check_preference_exists(TEST_URI, false);
-}
-
-function test_content_preferences_cleared_with_subdomain()
-{
-  const TEST_URI = uri("http://www.mozilla.org");
-  add_preference(TEST_URI);
-  pb.removeDataFromDomain("mozilla.org");
-  check_preference_exists(TEST_URI, false);
-}
-
-function test_content_preferecnes_not_cleared_with_uri_contains_domain()
-{
-  const TEST_URI = uri("http://ilovemozilla.org");
-  add_preference(TEST_URI);
-  pb.removeDataFromDomain("mozilla.org");
-  check_preference_exists(TEST_URI, true);
-
-  // Reset state
-  let cp = Cc["@mozilla.org/content-pref/service;1"].
-           getService(Ci.nsIContentPrefService);
-  cp.removePref(TEST_URI, PREFERENCE_NAME);
-  check_preference_exists(TEST_URI, false);
-}
-
-// Cache
-function test_cache_cleared()
-{
-  // Because this test is asynchronous, it should be the last test
-  do_check_eq(tests[tests.length - 1], arguments.callee);
-
-  // NOTE: We could be more extensive with this test and actually add an entry
-  //       to the cache, and then make sure it is gone.  However, we trust that
-  //       the API is well tested, and that when we get the observer
-  //       notification, we have actually cleared the cache.
-  // This seems to happen asynchronously...
-  let os = Cc["@mozilla.org/observer-service;1"].
-           getService(Ci.nsIObserverService);
-  let observer = {
-    observe: function(aSubject, aTopic, aData)
-    {
-      os.removeObserver(observer, "cacheservice:empty-cache");
-      do_test_finished();
-    }
-  };
-  os.addObserver(observer, "cacheservice:empty-cache", false);
-  pb.removeDataFromDomain("mozilla.org");
-  do_test_pending();
-}
-
-function test_storage_cleared()
-{
-  function getStorageForURI(aURI)
-  {
-    let principal = Cc["@mozilla.org/scriptsecuritymanager;1"].
-                    getService(Ci.nsIScriptSecurityManager).
-                    getNoAppCodebasePrincipal(aURI);
-    let dsm = Cc["@mozilla.org/dom/storagemanager;1"].
-              getService(Ci.nsIDOMStorageManager);
-    return dsm.getLocalStorageForPrincipal(principal, "");
-  }
-
-  let s = [
-    getStorageForURI(uri("http://mozilla.org")),
-    getStorageForURI(uri("http://my.mozilla.org")),
-    getStorageForURI(uri("http://ilovemozilla.org")),
-  ];
-
-  for (let i = 0; i < s.length; ++i) {
-    let storage = s[i];
-    storage.setItem("test", "value" + i);
-    do_check_eq(storage.length, 1);
-    do_check_eq(storage.key(0), "test");
-    do_check_eq(storage.getItem("test"), "value" + i);
-  }
-
-  pb.removeDataFromDomain("mozilla.org");
-
-  do_check_eq(s[0].getItem("test"), null);
-  do_check_eq(s[0].length, 0);
-  do_check_eq(s[1].getItem("test"), null);
-  do_check_eq(s[1].length, 0);
-  do_check_eq(s[2].getItem("test"), "value2");
-  do_check_eq(s[2].length, 1);
-}
-
-let tests = [
-  // History
-  test_history_cleared_with_direct_match,
-  test_history_cleared_with_subdomain,
-  test_history_not_cleared_with_uri_contains_domain,
-
-  // Cookie Service
-  test_cookie_cleared_with_direct_match,
-  test_cookie_cleared_with_subdomain,
-  test_cookie_not_cleared_with_uri_contains_domain,
-
-  // Download Manager
-  // Note: active downloads tested in test_removeDataFromDomain_activeDownloads.js
-  test_download_history_cleared_with_direct_match,
-  test_download_history_cleared_with_subdomain,
-  test_download_history_not_cleared_with_active_direct_match,
-
-  // Login Manager
-  test_login_manager_disabled_hosts_cleared_with_direct_match,
-  test_login_manager_disabled_hosts_cleared_with_subdomain,
-  test_login_manager_disabled_hosts_not_cleared_with_uri_contains_domain,
-  test_login_manager_logins_cleared_with_direct_match,
-  test_login_manager_logins_cleared_with_subdomain,
-  tets_login_manager_logins_not_cleared_with_uri_contains_domain,
-
-  // Permission Manager
-  test_permission_manager_cleared_with_direct_match,
-  test_permission_manager_cleared_with_subdomain,
-  test_permission_manager_not_cleared_with_uri_contains_domain,
-
-  // Content Preferences
-  test_content_preferences_cleared_with_direct_match,
-  test_content_preferences_cleared_with_subdomain,
-  test_content_preferecnes_not_cleared_with_uri_contains_domain,
-
-  // Storage
-  test_storage_cleared,
-
-  // Cache
-  test_cache_cleared,
-];
-
-function do_test()
-{
-  for (let i = 0; i < tests.length; i++)
-    tests[i]();
-}
deleted file mode 100644
--- a/browser/components/privatebrowsing/test/unit/do_test_removeDataFromDomain_activeDownloads.js
+++ /dev/null
@@ -1,125 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- * vim: sw=2 ts=2 sts=2
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-/**
- * Test added with bug 460086 to test the behavior of the new API that was added
- * to remove all traces of visiting a site.
- */
-
-////////////////////////////////////////////////////////////////////////////////
-//// Constants
-
-let pb = Cc[PRIVATEBROWSING_CONTRACT_ID].
-         getService(Ci.nsIPrivateBrowsingService);
-
-////////////////////////////////////////////////////////////////////////////////
-//// Utility Functions
-
-/**
- * Creates an nsIURI object for the given file.
- *
- * @param aFile
- *        The nsIFile of the URI to create.
- * @returns an nsIURI representing aFile.
- */
-function uri(aFile)
-{
-  return Cc["@mozilla.org/network/io-service;1"].
-         getService(Ci.nsIIOService).
-         newFileURI(aFile);
-}
-
-/**
- * Checks to ensure a URI string is in download history or not.
- *
- * @param aURIString
- *        The string of the URI to check.
- * @param aIsActive
- *        True if the URI should be actively downloaded, false otherwise.
- */
-function check_active_download(aURIString, aIsActive)
-{
-  let dm = Cc["@mozilla.org/download-manager;1"].
-           getService(Ci.nsIDownloadManager);
-  let enumerator = dm.activeDownloads;
-  let found = false;
-  while (enumerator.hasMoreElements()) {
-    let dl = enumerator.getNext().QueryInterface(Ci.nsIDownload);
-    if (dl.source.spec == aURIString)
-      found = true;
-  }
-  let checker = aIsActive ? do_check_true : do_check_false;
-  checker(found);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-//// Test Functions
-
-let destFile = dirSvc.get("TmpD", Ci.nsIFile);
-destFile.append("dm-test-file");
-destFile = uri(destFile);
-let data = [
-  { source: "http://mozilla.org/direct_match",
-    target: destFile.spec,
-    removed: true
-  },
-  { source: "http://www.mozilla.org/subdomain",
-    target: destFile.spec,
-    removed: true
-  },
-  { source: "http://ilovemozilla.org/contains_domain",
-    target: destFile.spec,
-    removed: false
-  },
-];
-
-function do_test()
-{
-  // We add this data to the database first, but we cannot instantiate the
-  // download manager service, otherwise these downloads will not be placed in
-  // the active downloads array.
-
-  // Copy the empty downloads database to our profile directory
-  let downloads = do_get_file("downloads.empty.sqlite");
-  downloads.copyTo(dirSvc.get("ProfD", Ci.nsIFile), "downloads.sqlite");
-
-  // Open the database
-  let ss = Cc["@mozilla.org/storage/service;1"].
-           getService(Ci.mozIStorageService);
-  let file = dirSvc.get("ProfD", Ci.nsIFile);
-  file.append("downloads.sqlite");
-  let db = ss.openDatabase(file);
-
-  // Insert the data
-  let stmt = db.createStatement(
-    "INSERT INTO moz_downloads (source, target, state, autoResume, entityID) " +
-    "VALUES (:source, :target, :state, :autoResume, :entityID)"
-  );
-  for (let i = 0; i < data.length; i++) {
-    stmt.params.source = data[i].source;
-    stmt.params.target = data[i].target;
-    stmt.params.state = Ci.nsIDownloadManager.DOWNLOAD_PAUSED;
-    stmt.params.autoResume = 0; // DONT_RESUME is 0
-    stmt.params.entityID = "foo" // just has to be non-null for our test
-    stmt.execute();
-    stmt.reset();
-  }
-  stmt.finalize();
-  stmt = null;
-  db.close();
-  db = null;
-
-  // Check to make sure it's all there
-  for (let i = 0; i < data.length; i++)
-    check_active_download(data[i].source, true);
-
-  // Dispatch the remove call
-  pb.removeDataFromDomain("mozilla.org");
-
-  // And check our data
-  for (let i = 0; i < data.length; i++)
-    check_active_download(data[i].source, !data[i].removed);
-}
deleted file mode 100644
--- a/browser/components/privatebrowsing/test/unit/test_privatebrowsingwrapper_removeDataFromDomain.js
+++ /dev/null
@@ -1,21 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- * vim: sw=2 ts=2 sts=2
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-/**
- * Test added with bug 460086 to test the behavior of the new API that was added
- * to remove all traces of visiting a site.
- */
-
-Components.utils.import("resource://gre/modules/Services.jsm");
-
-function run_test() {
-  PRIVATEBROWSING_CONTRACT_ID = "@mozilla.org/privatebrowsing-wrapper;1";
-  load("do_test_removeDataFromDomain.js");
-  do_test();
-
-  // Shutdown the download manager.
-  Services.obs.notifyObservers(null, "quit-application", null);
-}
deleted file mode 100644
--- a/browser/components/privatebrowsing/test/unit/test_privatebrowsingwrapper_removeDataFromDomain_activeDownloads.js
+++ /dev/null
@@ -1,21 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- * vim: sw=2 ts=2 sts=2
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-/**
- * Test added with bug 460086 to test the behavior of the new API that was added
- * to remove all traces of visiting a site.
- */
-
-Components.utils.import("resource://gre/modules/Services.jsm");
-
-function run_test() {
-  PRIVATEBROWSING_CONTRACT_ID = "@mozilla.org/privatebrowsing;1";
-  load("do_test_removeDataFromDomain_activeDownloads.js");
-  do_test();
-
-  // Shutdown the download manager.
-  Services.obs.notifyObservers(null, "quit-application", null);
-}
--- a/browser/components/privatebrowsing/test/unit/test_removeDataFromDomain.js
+++ b/browser/components/privatebrowsing/test/unit/test_removeDataFromDomain.js
@@ -4,18 +4,629 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /**
  * Test added with bug 460086 to test the behavior of the new API that was added
  * to remove all traces of visiting a site.
  */
 
-Components.utils.import("resource://gre/modules/Services.jsm");
+////////////////////////////////////////////////////////////////////////////////
+//// Globals
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/PlacesUtils.jsm");
+Cu.import("resource://gre/modules/ClearRecentHistory.jsm");
+
+const COOKIE_EXPIRY = Math.round(Date.now() / 1000) + 60;
+const COOKIE_NAME = "testcookie";
+const COOKIE_PATH = "/";
+
+const LOGIN_USERNAME = "username";
+const LOGIN_PASSWORD = "password";
+const LOGIN_USERNAME_FIELD = "username_field";
+const LOGIN_PASSWORD_FIELD = "password_field";
+
+const PERMISSION_TYPE = "test-perm";
+const PERMISSION_VALUE = Ci.nsIPermissionManager.ALLOW_ACTION;
+
+const PREFERENCE_NAME = "test-pref";
+
+////////////////////////////////////////////////////////////////////////////////
+//// Utility Functions
+
+/**
+ * Creates an nsIURI object for the given string representation of a URI.
+ *
+ * @param aURIString
+ *        The spec of the URI to create.
+ * @returns an nsIURI representing aURIString.
+ */
+function uri(aURIString)
+{
+  return Cc["@mozilla.org/network/io-service;1"].
+         getService(Ci.nsIIOService).
+         newURI(aURIString, null, null);
+}
+
+/**
+ * Adds a visit to history.
+ *
+ * @param aURI
+ *        The URI to add.
+ */
+function add_visit(aURI)
+{
+  check_visited(aURI, false);
+  PlacesUtils.history.addVisit(aURI, Date.now() * 1000, null,
+                               Ci.nsINavHistoryService.TRANSITION_LINK, false,
+                               0);
+  check_visited(aURI, true);
+}
+
+/**
+ * Checks to ensure a URI string is visited or not.
+ *
+ * @param aURI
+ *        The URI to check.
+ * @param aIsVisited
+ *        True if the URI should be visited, false otherwise.
+ */
+function check_visited(aURI, aIsVisited)
+{
+  let checker = aIsVisited ? do_check_true : do_check_false;
+  checker(PlacesUtils.ghistory2.isVisited(aURI));
+}
+
+/**
+ * Add a cookie to the cookie service.
+ *
+ * @param aDomain
+ */
+function add_cookie(aDomain)
+{
+  check_cookie_exists(aDomain, false);
+  let cm = Cc["@mozilla.org/cookiemanager;1"].getService(Ci.nsICookieManager2);
+  cm.add(aDomain, COOKIE_PATH, COOKIE_NAME, "", false, false, false,
+         COOKIE_EXPIRY);
+  check_cookie_exists(aDomain, true);
+}
+
+/**
+ * Checks to ensure that a cookie exists or not for a domain.
+ *
+ * @param aDomain
+ *        The domain to check for the cookie.
+ * @param aExists
+ *        True if the cookie should exist, false otherwise.
+ */
+function check_cookie_exists(aDomain, aExists)
+{
+  let cm = Cc["@mozilla.org/cookiemanager;1"].getService(Ci.nsICookieManager2);
+  let cookie = {
+    host: aDomain,
+    name: COOKIE_NAME,
+    path: COOKIE_PATH
+  }
+  let checker = aExists ? do_check_true : do_check_false;
+  checker(cm.cookieExists(cookie));
+}
+
+/**
+ * Adds a download to download history.
+ *
+ * @param aURIString
+ *        The string of the URI to add.
+ * @param aIsActive
+ *        If it should be set to an active state in the database.  This does not
+ *        make it show up in the list of active downloads however!
+ */
+function add_download(aURIString, aIsActive)
+{
+  check_downloaded(aURIString, false);
+  let db = Cc["@mozilla.org/download-manager;1"].
+           getService(Ci.nsIDownloadManager).
+           DBConnection;
+  let stmt = db.createStatement(
+    "INSERT INTO moz_downloads (source, state) " +
+    "VALUES (:source, :state)"
+  );
+  stmt.params.source = aURIString;
+  stmt.params.state = aIsActive ? Ci.nsIDownloadManager.DOWNLOAD_DOWNLOADING :
+                                  Ci.nsIDownloadManager.DOWNLOAD_FINISHED;
+  try {
+    stmt.execute();
+  }
+  finally {
+    stmt.finalize();
+  }
+  check_downloaded(aURIString, true);
+}
+
+/**
+ * Checks to ensure a URI string is in download history or not.
+ *
+ * @param aURIString
+ *        The string of the URI to check.
+ * @param aIsDownloaded
+ *        True if the URI should be downloaded, false otherwise.
+ */
+function check_downloaded(aURIString, aIsDownloaded)
+{
+  let db = Cc["@mozilla.org/download-manager;1"].
+           getService(Ci.nsIDownloadManager).
+           DBConnection;
+  let stmt = db.createStatement(
+    "SELECT * " +
+    "FROM moz_downloads " +
+    "WHERE source = :source"
+  );
+  stmt.params.source = aURIString;
+
+  let checker = aIsDownloaded ? do_check_true : do_check_false;
+  try {
+    checker(stmt.executeStep());
+  }
+  finally {
+    stmt.finalize();
+  }
+}
+
+/**
+ * Adds a disabled host to the login manager.
+ *
+ * @param aHost
+ *        The host to add to the list of disabled hosts.
+ */
+function add_disabled_host(aHost)
+{
+  check_disabled_host(aHost, false);
+  let lm = Cc["@mozilla.org/login-manager;1"].
+           getService(Ci.nsILoginManager);
+  lm.setLoginSavingEnabled(aHost, false);
+  check_disabled_host(aHost, true);
+}
+
+/**
+ * Checks to see if a host is disabled for storing logins or not.
+ *
+ * @param aHost
+ *        The host to check if it is disabled.
+ * @param aIsDisabled
+ *        True if the host should be disabled, false otherwise.
+ */
+function check_disabled_host(aHost, aIsDisabled)
+{
+  let lm = Cc["@mozilla.org/login-manager;1"].
+           getService(Ci.nsILoginManager);
+  let checker = aIsDisabled ? do_check_false : do_check_true;
+  checker(lm.getLoginSavingEnabled(aHost));
+}
+
+/**
+ * Adds a login for the specified host to the login manager.
+ *
+ * @param aHost
+ *        The host to add the login for.
+ */
+function add_login(aHost)
+{
+  check_login_exists(aHost, false);
+  let login = Cc["@mozilla.org/login-manager/loginInfo;1"].
+              createInstance(Ci.nsILoginInfo);
+  login.init(aHost, "", null, LOGIN_USERNAME, LOGIN_PASSWORD,
+             LOGIN_USERNAME_FIELD, LOGIN_PASSWORD_FIELD);
+  let lm = Cc["@mozilla.org/login-manager;1"].
+           getService(Ci.nsILoginManager);
+  lm.addLogin(login);
+  check_login_exists(aHost, true);
+}
+
+/**
+ * Checks to see if a login exists for a host.
+ *
+ * @param aHost
+ *        The host to check for the test login.
+ * @param aExists
+ *        True if the login should exist, false otherwise.
+ */
+function check_login_exists(aHost, aExists)
+{
+  let lm = Cc["@mozilla.org/login-manager;1"].
+           getService(Ci.nsILoginManager);
+  let count = { value: 0 };
+  lm.findLogins(count, aHost, "", null);
+  do_check_eq(count.value, aExists ? 1 : 0);
+}
+
+/**
+ * Adds a permission for the specified URI to the permission manager.
+ *
+ * @param aURI
+ *        The URI to add the test permission for.
+ */
+function add_permission(aURI)
+{
+  check_permission_exists(aURI, false);
+  let pm = Cc["@mozilla.org/permissionmanager;1"].
+           getService(Ci.nsIPermissionManager);
+  let principal = Cc["@mozilla.org/scriptsecuritymanager;1"]
+                    .getService(Ci.nsIScriptSecurityManager)
+                    .getNoAppCodebasePrincipal(aURI);
+
+  pm.addFromPrincipal(principal, PERMISSION_TYPE, PERMISSION_VALUE);
+  check_permission_exists(aURI, true);
+}
+
+/**
+ * Checks to see if a permission exists for the given URI.
+ *
+ * @param aURI
+ *        The URI to check if a permission exists.
+ * @param aExists
+ *        True if the permission should exist, false otherwise.
+ */
+function check_permission_exists(aURI, aExists)
+{
+  let pm = Cc["@mozilla.org/permissionmanager;1"].
+           getService(Ci.nsIPermissionManager);
+  let principal = Cc["@mozilla.org/scriptsecuritymanager;1"]
+                    .getService(Ci.nsIScriptSecurityManager)
+                    .getNoAppCodebasePrincipal(aURI);
+
+  let perm = pm.testExactPermissionFromPrincipal(principal, PERMISSION_TYPE);
+  let checker = aExists ? do_check_eq : do_check_neq;
+  checker(perm, PERMISSION_VALUE);
+}
+
+/**
+ * Adds a content preference for the specified URI.
+ *
+ * @param aURI
+ *        The URI to add a preference for.
+ */
+function add_preference(aURI)
+{
+  check_preference_exists(aURI, false);
+  let cp = Cc["@mozilla.org/content-pref/service;1"].
+           getService(Ci.nsIContentPrefService);
+  cp.setPref(aURI, PREFERENCE_NAME, "foo");
+  check_preference_exists(aURI, true);
+}
+
+/**
+ * Checks to see if a preference exists for the given URI.
+ *
+ * @param aURI
+ *        The URI to check if a preference exists.
+ * @param aExists
+ *        True if the permission should exist, false otherwise.
+ */
+function check_preference_exists(aURI, aExists)
+{
+  let cp = Cc["@mozilla.org/content-pref/service;1"].
+           getService(Ci.nsIContentPrefService);
+  let checker = aExists ? do_check_true : do_check_false;
+  checker(cp.hasPref(aURI, PREFERENCE_NAME));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//// Test Functions
 
-function run_test() {
-  PRIVATEBROWSING_CONTRACT_ID = "@mozilla.org/privatebrowsing;1";
-  load("do_test_removeDataFromDomain.js");
-  do_test();
+// History
+function test_history_cleared_with_direct_match()
+{
+  const TEST_URI = uri("http://mozilla.org/foo");
+  add_visit(TEST_URI);
+  ClearRecentHistory.removeDataFromDomain("mozilla.org");
+  check_visited(TEST_URI, false);
+}
+
+function test_history_cleared_with_subdomain()
+{
+  const TEST_URI = uri("http://www.mozilla.org/foo");
+  add_visit(TEST_URI);
+  ClearRecentHistory.removeDataFromDomain("mozilla.org");
+  check_visited(TEST_URI, false);
+}
+
+function test_history_not_cleared_with_uri_contains_domain()
+{
+  const TEST_URI = uri("http://ilovemozilla.org/foo");
+  add_visit(TEST_URI);
+  ClearRecentHistory.removeDataFromDomain("mozilla.org");
+  check_visited(TEST_URI, true);
+
+  // Clear history since we left something there from this test.
+  PlacesUtils.bhistory.removeAllPages();
+}
+
+// Cookie Service
+function test_cookie_cleared_with_direct_match()
+{
+  const TEST_DOMAIN = "mozilla.org";
+  add_cookie(TEST_DOMAIN);
+  ClearRecentHistory.removeDataFromDomain("mozilla.org");
+  check_cookie_exists(TEST_DOMAIN, false);
+}
+
+function test_cookie_cleared_with_subdomain()
+{
+  const TEST_DOMAIN = "www.mozilla.org";
+  add_cookie(TEST_DOMAIN);
+  ClearRecentHistory.removeDataFromDomain("mozilla.org");
+  check_cookie_exists(TEST_DOMAIN, false);
+}
+
+function test_cookie_not_cleared_with_uri_contains_domain()
+{
+  const TEST_DOMAIN = "ilovemozilla.org";
+  add_cookie(TEST_DOMAIN);
+  ClearRecentHistory.removeDataFromDomain("mozilla.org");
+  check_cookie_exists(TEST_DOMAIN, true);
+}
+
+// Download Manager
+function test_download_history_cleared_with_direct_match()
+{
+  const TEST_URI = "http://mozilla.org/foo";
+  add_download(TEST_URI, false);
+  ClearRecentHistory.removeDataFromDomain("mozilla.org");
+  check_downloaded(TEST_URI, false);
+}
+
+function test_download_history_cleared_with_subdomain()
+{
+  const TEST_URI = "http://www.mozilla.org/foo";
+  add_download(TEST_URI, false);
+  ClearRecentHistory.removeDataFromDomain("mozilla.org");
+  check_downloaded(TEST_URI, false);
+}
+
+function test_download_history_not_cleared_with_active_direct_match()
+{
+  // Tests that downloads marked as active in the db are not deleted from the db
+  const TEST_URI = "http://mozilla.org/foo";
+  add_download(TEST_URI, true);
+  ClearRecentHistory.removeDataFromDomain("mozilla.org");
+  check_downloaded(TEST_URI, true);
+
+  // Reset state
+  let db = Cc["@mozilla.org/download-manager;1"].
+           getService(Ci.nsIDownloadManager).
+           DBConnection;
+  db.executeSimpleSQL("DELETE FROM moz_downloads");
+  check_downloaded(TEST_URI, false);
+}
+
+// Login Manager
+function test_login_manager_disabled_hosts_cleared_with_direct_match()
+{
+  const TEST_HOST = "http://mozilla.org";
+  add_disabled_host(TEST_HOST);
+  ClearRecentHistory.removeDataFromDomain("mozilla.org");
+  check_disabled_host(TEST_HOST, false);
+}
+
+function test_login_manager_disabled_hosts_cleared_with_subdomain()
+{
+  const TEST_HOST = "http://www.mozilla.org";
+  add_disabled_host(TEST_HOST);
+  ClearRecentHistory.removeDataFromDomain("mozilla.org");
+  check_disabled_host(TEST_HOST, false);
+}
+
+function test_login_manager_disabled_hosts_not_cleared_with_uri_contains_domain()
+{
+  const TEST_HOST = "http://ilovemozilla.org";
+  add_disabled_host(TEST_HOST);
+  ClearRecentHistory.removeDataFromDomain("mozilla.org");
+  check_disabled_host(TEST_HOST, true);
+
+  // Reset state
+  let lm = Cc["@mozilla.org/login-manager;1"].
+           getService(Ci.nsILoginManager);
+  lm.setLoginSavingEnabled(TEST_HOST, true);
+  check_disabled_host(TEST_HOST, false);
+}
+
+function test_login_manager_logins_cleared_with_direct_match()
+{
+  const TEST_HOST = "http://mozilla.org";
+  add_login(TEST_HOST);
+  ClearRecentHistory.removeDataFromDomain("mozilla.org");
+  check_login_exists(TEST_HOST, false);
+}
+
+function test_login_manager_logins_cleared_with_subdomain()
+{
+  const TEST_HOST = "http://www.mozilla.org";
+  add_login(TEST_HOST);
+  ClearRecentHistory.removeDataFromDomain("mozilla.org");
+  check_login_exists(TEST_HOST, false);
+}
+
+function tets_login_manager_logins_not_cleared_with_uri_contains_domain()
+{
+  const TEST_HOST = "http://ilovemozilla.org";
+  add_login(TEST_HOST);
+  ClearRecentHistory.removeDataFromDomain("mozilla.org");
+  check_login_exists(TEST_HOST, true);
+
+  let lm = Cc["@mozilla.org/login-manager;1"].
+           getService(Ci.nsILoginManager);
+  lm.removeAllLogins();
+  check_login_exists(TEST_HOST, false);
+}
+
+// Permission Manager
+function test_permission_manager_cleared_with_direct_match()
+{
+  const TEST_URI = uri("http://mozilla.org");
+  add_permission(TEST_URI);
+  ClearRecentHistory.removeDataFromDomain("mozilla.org");
+  check_permission_exists(TEST_URI, false);
+}
+
+function test_permission_manager_cleared_with_subdomain()
+{
+  const TEST_URI = uri("http://www.mozilla.org");
+  add_permission(TEST_URI);
+  ClearRecentHistory.removeDataFromDomain("mozilla.org");
+  check_permission_exists(TEST_URI, false);
+}
+
+function test_permission_manager_not_cleared_with_uri_contains_domain()
+{
+  const TEST_URI = uri("http://ilovemozilla.org");
+  add_permission(TEST_URI);
+  ClearRecentHistory.removeDataFromDomain("mozilla.org");
+  check_permission_exists(TEST_URI, true);
+
+  // Reset state
+  let pm = Cc["@mozilla.org/permissionmanager;1"].
+           getService(Ci.nsIPermissionManager);
+  pm.removeAll();
+  check_permission_exists(TEST_URI, false);
+}
+
+// Content Preferences
+function test_content_preferences_cleared_with_direct_match()
+{
+  const TEST_URI = uri("http://mozilla.org");
+  add_preference(TEST_URI);
+  ClearRecentHistory.removeDataFromDomain("mozilla.org");
+  check_preference_exists(TEST_URI, false);
+}
+
+function test_content_preferences_cleared_with_subdomain()
+{
+  const TEST_URI = uri("http://www.mozilla.org");
+  add_preference(TEST_URI);
+  ClearRecentHistory.removeDataFromDomain("mozilla.org");
+  check_preference_exists(TEST_URI, false);
+}
+
+function test_content_preferecnes_not_cleared_with_uri_contains_domain()
+{
+  const TEST_URI = uri("http://ilovemozilla.org");
+  add_preference(TEST_URI);
+  ClearRecentHistory.removeDataFromDomain("mozilla.org");
+  check_preference_exists(TEST_URI, true);
+
+  // Reset state
+  let cp = Cc["@mozilla.org/content-pref/service;1"].
+           getService(Ci.nsIContentPrefService);
+  cp.removePref(TEST_URI, PREFERENCE_NAME);
+  check_preference_exists(TEST_URI, false);
+}
+
+// Cache
+function test_cache_cleared()
+{
+  // Because this test is asynchronous, it should be the last test
+  do_check_eq(tests[tests.length - 1], arguments.callee);
+
+  // NOTE: We could be more extensive with this test and actually add an entry
+  //       to the cache, and then make sure it is gone.  However, we trust that
+  //       the API is well tested, and that when we get the observer
+  //       notification, we have actually cleared the cache.
+  // This seems to happen asynchronously...
+  let os = Cc["@mozilla.org/observer-service;1"].
+           getService(Ci.nsIObserverService);
+  let observer = {
+    observe: function(aSubject, aTopic, aData)
+    {
+      os.removeObserver(observer, "cacheservice:empty-cache");
+      do_test_finished();
+    }
+  };
+  os.addObserver(observer, "cacheservice:empty-cache", false);
+  ClearRecentHistory.removeDataFromDomain("mozilla.org");
+  do_test_pending();
+}
+
+function test_storage_cleared()
+{
+  function getStorageForURI(aURI)
+  {
+    let principal = Cc["@mozilla.org/scriptsecuritymanager;1"].
+                    getService(Ci.nsIScriptSecurityManager).
+                    getNoAppCodebasePrincipal(aURI);
+    let dsm = Cc["@mozilla.org/dom/storagemanager;1"].
+              getService(Ci.nsIDOMStorageManager);
+    return dsm.getLocalStorageForPrincipal(principal, "");
+  }
+
+  let s = [
+    getStorageForURI(uri("http://mozilla.org")),
+    getStorageForURI(uri("http://my.mozilla.org")),
+    getStorageForURI(uri("http://ilovemozilla.org")),
+  ];
+
+  for (let i = 0; i < s.length; ++i) {
+    let storage = s[i];
+    storage.setItem("test", "value" + i);
+    do_check_eq(storage.length, 1);
+    do_check_eq(storage.key(0), "test");
+    do_check_eq(storage.getItem("test"), "value" + i);
+  }
+
+  ClearRecentHistory.removeDataFromDomain("mozilla.org");
+
+  do_check_eq(s[0].getItem("test"), null);
+  do_check_eq(s[0].length, 0);
+  do_check_eq(s[1].getItem("test"), null);
+  do_check_eq(s[1].length, 0);
+  do_check_eq(s[2].getItem("test"), "value2");
+  do_check_eq(s[2].length, 1);
+}
+
+let tests = [
+  // History
+  test_history_cleared_with_direct_match,
+  test_history_cleared_with_subdomain,
+  test_history_not_cleared_with_uri_contains_domain,
+
+  // Cookie Service
+  test_cookie_cleared_with_direct_match,
+  test_cookie_cleared_with_subdomain,
+  test_cookie_not_cleared_with_uri_contains_domain,
+
+  // Download Manager
+  // Note: active downloads tested in test_removeDataFromDomain_activeDownloads.js
+  test_download_history_cleared_with_direct_match,
+  test_download_history_cleared_with_subdomain,
+  test_download_history_not_cleared_with_active_direct_match,
+
+  // Login Manager
+  test_login_manager_disabled_hosts_cleared_with_direct_match,
+  test_login_manager_disabled_hosts_cleared_with_subdomain,
+  test_login_manager_disabled_hosts_not_cleared_with_uri_contains_domain,
+  test_login_manager_logins_cleared_with_direct_match,
+  test_login_manager_logins_cleared_with_subdomain,
+  tets_login_manager_logins_not_cleared_with_uri_contains_domain,
+
+  // Permission Manager
+  test_permission_manager_cleared_with_direct_match,
+  test_permission_manager_cleared_with_subdomain,
+  test_permission_manager_not_cleared_with_uri_contains_domain,
+
+  // Content Preferences
+  test_content_preferences_cleared_with_direct_match,
+  test_content_preferences_cleared_with_subdomain,
+  test_content_preferecnes_not_cleared_with_uri_contains_domain,
+
+  // Storage
+  test_storage_cleared,
+
+  // Cache
+  test_cache_cleared,
+];
+
+function run_test()
+{
+  for (let i = 0; i < tests.length; i++)
+    tests[i]();
 
   // Shutdown the download manager.
   Services.obs.notifyObservers(null, "quit-application", null);
 }
--- a/browser/components/privatebrowsing/test/unit/test_removeDataFromDomain_activeDownloads.js
+++ b/browser/components/privatebrowsing/test/unit/test_removeDataFromDomain_activeDownloads.js
@@ -5,17 +5,121 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /**
  * Test added with bug 460086 to test the behavior of the new API that was added
  * to remove all traces of visiting a site.
  */
 
 Components.utils.import("resource://gre/modules/Services.jsm");
+Components.utils.import("resource://gre/modules/ClearRecentHistory.jsm");
 
-function run_test() {
-  PRIVATEBROWSING_CONTRACT_ID = "@mozilla.org/privatebrowsing;1";
-  load("do_test_removeDataFromDomain_activeDownloads.js");
-  do_test();
+////////////////////////////////////////////////////////////////////////////////
+//// Utility Functions
+
+/**
+ * Creates an nsIURI object for the given file.
+ *
+ * @param aFile
+ *        The nsIFile of the URI to create.
+ * @returns an nsIURI representing aFile.
+ */
+function uri(aFile)
+{
+  return Cc["@mozilla.org/network/io-service;1"].
+         getService(Ci.nsIIOService).
+         newFileURI(aFile);
+}
+
+/**
+ * Checks to ensure a URI string is in download history or not.
+ *
+ * @param aURIString
+ *        The string of the URI to check.
+ * @param aIsActive
+ *        True if the URI should be actively downloaded, false otherwise.
+ */
+function check_active_download(aURIString, aIsActive)
+{
+  let dm = Cc["@mozilla.org/download-manager;1"].
+           getService(Ci.nsIDownloadManager);
+  let enumerator = dm.activeDownloads;
+  let found = false;
+  while (enumerator.hasMoreElements()) {
+    let dl = enumerator.getNext().QueryInterface(Ci.nsIDownload);
+    if (dl.source.spec == aURIString)
+      found = true;
+  }
+  let checker = aIsActive ? do_check_true : do_check_false;
+  checker(found);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//// Test Functions
+
+let destFile = dirSvc.get("TmpD", Ci.nsIFile);
+destFile.append("dm-test-file");
+destFile = uri(destFile);
+let data = [
+  { source: "http://mozilla.org/direct_match",
+    target: destFile.spec,
+    removed: true
+  },
+  { source: "http://www.mozilla.org/subdomain",
+    target: destFile.spec,
+    removed: true
+  },
+  { source: "http://ilovemozilla.org/contains_domain",
+    target: destFile.spec,
+    removed: false
+  },
+];
+
+function run_test()
+{
+  // We add this data to the database first, but we cannot instantiate the
+  // download manager service, otherwise these downloads will not be placed in
+  // the active downloads array.
+
+  // Copy the empty downloads database to our profile directory
+  let downloads = do_get_file("downloads.empty.sqlite");
+  downloads.copyTo(dirSvc.get("ProfD", Ci.nsIFile), "downloads.sqlite");
+
+  // Open the database
+  let ss = Cc["@mozilla.org/storage/service;1"].
+           getService(Ci.mozIStorageService);
+  let file = dirSvc.get("ProfD", Ci.nsIFile);
+  file.append("downloads.sqlite");
+  let db = ss.openDatabase(file);
+
+  // Insert the data
+  let stmt = db.createStatement(
+    "INSERT INTO moz_downloads (source, target, state, autoResume, entityID) " +
+    "VALUES (:source, :target, :state, :autoResume, :entityID)"
+  );
+  for (let i = 0; i < data.length; i++) {
+    stmt.params.source = data[i].source;
+    stmt.params.target = data[i].target;
+    stmt.params.state = Ci.nsIDownloadManager.DOWNLOAD_PAUSED;
+    stmt.params.autoResume = 0; // DONT_RESUME is 0
+    stmt.params.entityID = "foo" // just has to be non-null for our test
+    stmt.execute();
+    stmt.reset();
+  }
+  stmt.finalize();
+  stmt = null;
+  db.close();
+  db = null;
+
+  // Check to make sure it's all there
+  for (let i = 0; i < data.length; i++)
+    check_active_download(data[i].source, true);
+
+  // Dispatch the remove call
+  ClearRecentHistory.removeDataFromDomain("mozilla.org");
+
+  // And check our data
+  for (let i = 0; i < data.length; i++)
+    check_active_download(data[i].source, !data[i].removed);
 
   // Shutdown the download manager.
   Services.obs.notifyObservers(null, "quit-application", null);
 }
--- a/browser/components/privatebrowsing/test/unit/xpcshell.ini
+++ b/browser/components/privatebrowsing/test/unit/xpcshell.ini
@@ -11,14 +11,12 @@ tail = tail_privatebrowsing.js
 [test_privatebrowsing_autostart.js]
 [test_privatebrowsing_commandline.js]
 [test_privatebrowsing_exit.js]
 [test_privatebrowsing_telemetry.js]
 [test_privatebrowsingwrapper_autostart.js]
 [test_privatebrowsingwrapper_commandline.js]
 [test_privatebrowsingwrapper_exit.js]
 [test_privatebrowsingwrapper_placesTitleNoUpdate.js]
-[test_privatebrowsingwrapper_removeDataFromDomain.js]
-[test_privatebrowsingwrapper_removeDataFromDomain_activeDownloads.js]
 [test_privatebrowsingwrapper_telemetry.js]
 [test_removeDataFromDomain.js]
 [test_removeDataFromDomain_activeDownloads.js]
 [test_transition_nooffline.js]
--- a/browser/components/sessionstore/test/browser_394759_purge.js
+++ b/browser/components/sessionstore/test/browser_394759_purge.js
@@ -1,15 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+Components.utils.import("resource://gre/modules/ClearRecentHistory.jsm");
+
 function test() {
-  let pb = Cc["@mozilla.org/privatebrowsing;1"].getService(Ci.nsIPrivateBrowsingService);
-
   // utility functions
   function countClosedTabsByTitle(aClosedTabList, aTitle)
     aClosedTabList.filter(function (aData) aData.title == aTitle).length;
 
   function countOpenTabsByTitle(aOpenTabList, aTitle)
     aOpenTabList.filter(function (aData) aData.entries.some(function (aEntry) aEntry.title == aTitle)).length
 
   // backup old state
@@ -74,17 +74,17 @@ function test() {
       }
     ]
   };
 
   // set browser to test state
   ss.setBrowserState(JSON.stringify(testState));
 
   // purge domain & check that we purged correctly for closed windows
-  pb.removeDataFromDomain("mozilla.org");
+  ClearRecentHistory.removeDataFromDomain("mozilla.org");
 
   let closedWindowData = JSON.parse(ss.getClosedWindowData());
 
   // First set of tests for _closedWindows[0] - tests basics
   let win = closedWindowData[0];
   is(win.tabs.length, 1, "1 tab was removed");
   is(countOpenTabsByTitle(win.tabs, FORGET), 0,
      "The correct tab was removed");
--- a/browser/components/sessionstore/test/browser_464199.js
+++ b/browser/components/sessionstore/test/browser_464199.js
@@ -1,12 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+Components.utils.import("resource://gre/modules/ClearRecentHistory.jsm");
+
 function test() {
   /** Test for Bug 464199 **/
 
   waitForExplicitFinish();
 
   const REMEMBER = Date.now(), FORGET = Math.random();
   let test_state = { windows: [{ "tabs": [{ "entries": [] }], _closedTabs: [
     { state: { entries: [{ url: "http://www.example.net/" }] }, title: FORGET },
@@ -51,19 +53,17 @@ function test() {
     is(closedTabs.length, test_state.windows[0]._closedTabs.length,
        "Closed tab list has the expected length");
     is(countByTitle(closedTabs, FORGET),
        test_state.windows[0]._closedTabs.length - remember_count,
        "The correct amout of tabs are to be forgotten");
     is(countByTitle(closedTabs, REMEMBER), remember_count,
        "Everything is set up.");
 
-    let pb = Cc["@mozilla.org/privatebrowsing;1"].
-             getService(Ci.nsIPrivateBrowsingService);
-    pb.removeDataFromDomain("example.net");
+    ClearRecentHistory.removeDataFromDomain("example.net");
 
     closedTabs = JSON.parse(ss.getClosedTabData(newWin));
     is(closedTabs.length, remember_count,
        "The correct amout of tabs was removed");
     is(countByTitle(closedTabs, FORGET), 0,
        "All tabs to be forgotten were indeed removed");
     is(countByTitle(closedTabs, REMEMBER), remember_count,
        "... and tabs to be remembered weren't.");
--- a/dom/indexedDB/test/browser_forgetThisSite.js
+++ b/dom/indexedDB/test/browser_forgetThisSite.js
@@ -1,13 +1,15 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
+Components.utils.import("resource://gre/modules/ClearRecentHistory.jsm");
+
 const domains = [
   "mochi.test:8888",
   "www.example.com"
 ];
 
 const addPath = "/browser/dom/indexedDB/test/browser_forgetThisSiteAdd.html";
 const getPath = "/browser/dom/indexedDB/test/browser_forgetThisSiteGet.html";
 
@@ -59,19 +61,17 @@ function test2()
     });
   }, true);
   content.location = testPageURL2;
 }
 
 function test3()
 {
   // Remove database from domain 2
-  Components.classes["@mozilla.org/privatebrowsing;1"]
-            .getService(Components.interfaces.nsIPrivateBrowsingService)
-            .removeDataFromDomain(domains[1]);
+  ClearRecentHistory.removeDataFromDomain(domains[1]);
   setPermission(testPageURL4, "indexedDB", "unknown");
   executeSoon(test4);
 }
 
 function test4()
 {
   // Get database version for domain 1
   gBrowser.selectedTab = gBrowser.addTab();
--- a/netwerk/base/public/nsIPrivateBrowsingService.idl
+++ b/netwerk/base/public/nsIPrivateBrowsingService.idl
@@ -1,15 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 
-[scriptable, uuid(4b731983-9542-49f4-b16b-de68ad1c2068)]
+[scriptable, uuid(b5a148a4-b422-478d-b475-194a126bbab1)]
 interface nsIPrivateBrowsingService : nsISupports
 {
     // When read, determines whether the private browsing mode is currently
     // active.  Setting to true enters the private browsing mode, and setting
     // to false leaves the private browsing mode.
     // Setting this value while handling one of the notifications generated
     // by the private browsing service throws NS_ERROR_FAILURE.
     attribute boolean privateBrowsingEnabled;
@@ -17,25 +17,16 @@ interface nsIPrivateBrowsingService : ns
     // Determine whether the private browsing mode has been started
     // automatically at application startup.
     // This value will never be true if privateBrowsingEnabled is false.
     readonly attribute boolean autoStarted;
 
     // Determine whether the last private browsing transition was performed through
     // the command line (using either the -private or -private-toggle switches).
     readonly attribute boolean lastChangedByCommandLine;
-
-    /**
-     * Removes all data stored for a given domain.  This includes all data for
-     * subdomains of the given domain.
-     *
-     * @param aDomain
-     *        The domain that will have its data removed.
-     */
-    void removeDataFromDomain(in AUTF8String aDomain);
 };
 
 %{C++
 /**
  * Private Browsing service notifications:
  *
  * - NS_PRIVATE_BROWSING_REQUEST_TOPIC:
  *   The data parameter determines which kind of request this represents: