Bug 669410 - Make the url-classifier PrefixSet persistent on startup/shutdown. r=tony
authorGian-Carlo Pascutto <gpascutto@mozilla.com>
Thu, 08 Sep 2011 22:16:59 +0200
changeset 76779 3034a31ec6ad854bd983e7e76cff7a2f4b1d6c16
parent 76778 ed57eb1fa5f6f84ee1bab2595de5fc419fba251d
child 76780 46f15935676ad5756a9fe9efbadd4ba2f8852c5b
push id3
push userfelipc@gmail.com
push dateFri, 30 Sep 2011 20:09:13 +0000
reviewerstony
bugs669410
milestone9.0a1
Bug 669410 - Make the url-classifier PrefixSet persistent on startup/shutdown. r=tony
toolkit/components/build/nsToolkitCompsCID.h
toolkit/components/url-classifier/nsIUrlClassifierPrefixSet.idl
toolkit/components/url-classifier/nsUrlClassifierDBService.cpp
toolkit/components/url-classifier/nsUrlClassifierPrefixSet.cpp
toolkit/components/url-classifier/nsUrlClassifierPrefixSet.h
--- a/toolkit/components/build/nsToolkitCompsCID.h
+++ b/toolkit/components/build/nsToolkitCompsCID.h
@@ -155,19 +155,19 @@
 // {59648a91-5a60-4122-8ff2-54b839c84aed}
 #define NS_PARENTALCONTROLSSERVICE_CID \
 { 0x580530e5, 0x118c, 0x4bc7, { 0xab, 0x88, 0xbc, 0x2c, 0xd2, 0xb9, 0x72, 0x23 } }
 
 // {e7f70966-9a37-48d7-8aeb-35998f31090e}
 #define NS_TYPEAHEADFIND_CID \
 { 0xe7f70966, 0x9a37, 0x48d7, { 0x8a, 0xeb, 0x35, 0x99, 0x8f, 0x31, 0x09, 0x0e} }
 
-// {6e9f759a-3f8d-4552-99ed-dbf0ea0a5f67}
+// {42ef1d52-3351-4973-98f8-d18f089bccfa}
 #define NS_URLCLASSIFIERPREFIXSET_CID \
-{ 0x6e9f759a, 0x3f8d, 0x4552, { 0x99, 0xed, 0xdb, 0xf0, 0xea, 0x0a, 0x5f, 0x67} }
+{ 0x42ef1d52, 0x3351, 0x4973, { 0x98, 0xf8, 0xd1, 0x8f, 0x08, 0x9b, 0xcc, 0xfa} }
 
 // {5eb7c3c1-ec1f-4007-87cc-eefb37d68ce6}
 #define NS_URLCLASSIFIERDBSERVICE_CID \
 { 0x5eb7c3c1, 0xec1f, 0x4007, { 0x87, 0xcc, 0xee, 0xfb, 0x37, 0xd6, 0x8c, 0xe6} }
 
 // {c2be6dc0-ef1e-4abd-86a2-4f864ddc57f6}
 #define NS_URLCLASSIFIERSTREAMUPDATER_CID \
 { 0xc2be6dc0, 0xef1e, 0x4abd, { 0x86, 0xa2, 0x4f, 0x86, 0x4d, 0xdc, 0x57, 0xf6} }
--- a/toolkit/components/url-classifier/nsIUrlClassifierPrefixSet.idl
+++ b/toolkit/components/url-classifier/nsIUrlClassifierPrefixSet.idl
@@ -1,15 +1,19 @@
 #include "nsISupports.idl"
+#include "nsIFile.idl"
 
 interface nsIArray;
 
-[scriptable, uuid(6e9f759a-3f8d-4552-99ed-dbf0ea0a5f67)]
+[scriptable, uuid(42ef1d52-3351-4973-98f8-d18f089bccfa)]
 interface nsIUrlClassifierPrefixSet : nsISupports
 {
   void setPrefixes([const, array, size_is(aLength)] in unsigned long aPrefixes,
                    in unsigned long aLength);
   void addPrefixes([const, array, size_is(aLength)] in unsigned long aPrefixes,
                    in unsigned long aLength);
   boolean contains(in unsigned long aPrefix);
   boolean probe(in unsigned long aPrefix, inout boolean aReady);
   PRUint32 estimateSize();
+  boolean isEmpty();
+  void loadFromFile(in nsIFile aFile);
+  void storeToFile(in nsIFile aFile);
 };
--- a/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp
+++ b/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp
@@ -133,16 +133,19 @@ static const PRLogModuleInfo *gUrlClassi
 #define DATABASE_FILENAME "urlclassifier3.sqlite"
 
 // The implementation version is updated during development when we
 // want to change schema, or to recover from updating bugs.  When an
 // implementation version change is detected, the database is scrapped
 // and we start over.
 #define IMPLEMENTATION_VERSION 7
 
+// Name of the persistent PrefixSet storage
+#define PREFIXSET_FILENAME  "urlclassifier.pset"
+
 #define MAX_HOST_COMPONENTS 5
 #define MAX_PATH_COMPONENTS 4
 
 // Updates will fail if fed chunks larger than this
 #define MAX_CHUNK_SIZE (1024 * 1024)
 
 // Prefs for implementing nsIURIClassifier to block page loads
 #define CHECK_MALWARE_PREF      "browser.safebrowsing.malware.enabled"
@@ -1187,24 +1190,26 @@ private:
   // Perform a classifier lookup for a given url.
   nsresult DoLookup(const nsACString& spec, nsIUrlClassifierLookupCallback* c);
 
   // Add entries to the results.
   nsresult AddNoise(PRInt64 nearID,
                     PRInt32 count,
                     nsTArray<nsUrlClassifierLookupResult>& results);
 
-  // Construct a Prefix Tree with known prefixes
-  nsresult ConstructPrefixTree();
+  // Construct a Prefix Set with known prefixes
+  nsresult LoadPrefixSet(nsCOMPtr<nsIFile> & aFile);
+  nsresult ConstructPrefixSet();
 
   // Set the SQLite cache size
   nsresult SetCacheSize(mozIStorageConnection * aConnection,
                         PRInt32 aCacheSize);
 
   nsCOMPtr<nsIFile> mDBFile;
+  nsCOMPtr<nsIFile> mPSFile;
 
   nsCOMPtr<nsICryptoHash> mCryptoHash;
 
   // Holds a connection to the Db.  We lazily initialize this because it has
   // to be created in the background thread (currently mozStorageConnection
   // isn't thread safe).
   nsCOMPtr<mozIStorageConnection> mConnection;
 
@@ -1355,19 +1360,25 @@ nsUrlClassifierDBServiceWorker::Init(PRI
 
   if (NS_FAILED(rv)) {
     rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
                                 getter_AddRefs(mDBFile));
   }
 
   if (NS_FAILED(rv)) return NS_ERROR_NOT_AVAILABLE;
 
+  rv = mDBFile->Clone(getter_AddRefs(mPSFile));
+  NS_ENSURE_SUCCESS(rv, rv);
+
   rv = mDBFile->Append(NS_LITERAL_STRING(DATABASE_FILENAME));
   NS_ENSURE_SUCCESS(rv, rv);
 
+  rv = mPSFile->Append(NS_LITERAL_STRING(PREFIXSET_FILENAME));
+  NS_ENSURE_SUCCESS(rv, rv);
+
   ResetUpdate();
 
   mTableFreshness.Init();
 
   return NS_OK;
 }
 
 nsresult
@@ -1394,17 +1405,17 @@ nsUrlClassifierDBService::CheckCleanHost
   NS_ENSURE_SUCCESS(rv, rv);
 
   *clean = PR_TRUE;
 
   for (PRUint32 i = 0; i < lookupHosts.Length(); i++) {
     nsUrlClassifierDomainHash hostKeyHash;
     hostKeyHash.FromPlaintext(lookupHosts[i], mHash);
 
-    // First probe the Prefix Tree for presence
+    // First probe the PrefixSet for presence
     PRUint32 domainkey = hostKeyHash.ToUint32() ^ ENCODE_DOMAIN_MAGIC;
 
     PRBool found;
     PRBool ready = PR_FALSE;  /* opportunistic probe */
     rv = mPrefixSet->Probe(domainkey, &ready, &found);
     NS_ENSURE_SUCCESS(rv, rv);
     LOG(("CheckCleanHost Probed %X ready: %d found: %d ",
          domainkey, ready, found));
@@ -3126,17 +3137,17 @@ nsUrlClassifierDBServiceWorker::ApplyUpd
       if (NS_SUCCEEDED(mUpdateStatus)) {
         mUpdateStatus = mConnection->CommitTransaction();
       }
     }
   }
 
   if (NS_SUCCEEDED(mUpdateStatus)) {
     // Reconstruct the prefix tree from the DB
-    nsresult rv = ConstructPrefixTree();
+    nsresult rv = ConstructPrefixSet();
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   if (mGrewCache) {
     // During the update we increased the page cache to bigger than we
     // want to keep around.  At the moment, the only reliable way to make
     // sure that the page cache is freed is to reopen the connection.
     LOG(("GrewCache true, reopening DB"));
@@ -3214,16 +3225,17 @@ nsUrlClassifierDBServiceWorker::ResetDat
 
   nsresult rv = CloseDb();
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = mPrefixSet->SetPrefixes(nsnull, 0);
   NS_ENSURE_SUCCESS(rv, rv);
 
   mDBFile->Remove(PR_FALSE);
+  mPSFile->Remove(PR_FALSE);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsUrlClassifierDBServiceWorker::CancelUpdate()
 {
   LOG(("CancelUpdate"));
@@ -3420,19 +3432,18 @@ nsUrlClassifierDBServiceWorker::OpenDb()
      getter_AddRefs(mInsertTableIdStatement));
   NS_ENSURE_SUCCESS(rv, rv);
 
   mConnection = connection;
 
   mCryptoHash = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  LOG(("SB construcing Prefix Tree\n"));
-
-  rv = ConstructPrefixTree();
+  LOG(("loading Prefix Set\n"));
+  rv = LoadPrefixSet(mPSFile);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 nsresult nsUrlClassifierStore::ReadPrefixes(nsTArray<PRUint32>& array)
 {
   mozStorageStatementScoper scoper(mAllPrefixStatement);
@@ -3490,32 +3501,65 @@ nsresult nsUrlClassifierStore::ReadPrefi
   }
 
   LOG(("SB domains: %d prefixes: %d fulldomain: %d\n", dcnt, pcnt, fcnt));
 
   return NS_OK;
 }
 
 nsresult
-nsUrlClassifierDBServiceWorker::ConstructPrefixTree()
+nsUrlClassifierDBServiceWorker::ConstructPrefixSet()
 {
   nsTArray<PRUint32> array;
   nsresult rv = mMainStore.ReadPrefixes(array);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // clear old tree
   rv = mPrefixSet->SetPrefixes(nsnull, 0);
   NS_ENSURE_SUCCESS(rv, rv);
   if (array.IsEmpty()) {
     // DB is empty, but put a sentinel to show that we looked
     array.AppendElement(0);
   }
   // construct new one
   rv = mPrefixSet->SetPrefixes(array.Elements(), array.Length());
   NS_ENSURE_SUCCESS(rv, rv);
+
+  // store the new tree to disk
+  rv = mPrefixSet->StoreToFile(mPSFile);
+  NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "failed to store the prefixset");
+
+  return NS_OK;
+}
+
+nsresult
+nsUrlClassifierDBServiceWorker::LoadPrefixSet(nsCOMPtr<nsIFile> & aFile)
+{
+  PRBool empty;
+  nsresult rv = mPrefixSet->IsEmpty(&empty);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (!empty) {
+    LOG(("PrefixSet already loaded, not loading again"));
+    return NS_OK;
+  }
+
+  PRBool exists;
+  rv = aFile->Exists(&exists);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (exists) {
+    LOG(("stored PrefixSet exists, loading from disk"));
+    rv = mPrefixSet->LoadFromFile(aFile);
+  }
+  if (!exists || NS_FAILED(rv)) {
+    LOG(("no (usable) stored PrefixSet found, constructing from store"));
+    ConstructPrefixSet();
+  }
+
 #ifdef DEBUG
   PRUint32 size = 0;
   rv = mPrefixSet->EstimateSize(&size);
   LOG(("SB tree done, size = %d bytes\n", size));
   NS_ENSURE_SUCCESS(rv, rv);
 #endif
 
   return NS_OK;
@@ -4305,16 +4349,17 @@ nsUrlClassifierDBService::Shutdown()
     prefs->RemoveObserver(CONFIRM_AGE_PREF, this);
   }
 
   nsresult rv;
   // First close the db connection.
   if (mWorker) {
     rv = mWorkerProxy->CancelUpdate();
     NS_ASSERTION(NS_SUCCEEDED(rv), "failed to post cancel update event");
+
     rv = mWorkerProxy->CloseDb();
     NS_ASSERTION(NS_SUCCEEDED(rv), "failed to post close db event");
   }
 
   mWorkerProxy = nsnull;
 
   LOG(("joining background thread"));
 
--- a/toolkit/components/url-classifier/nsUrlClassifierPrefixSet.cpp
+++ b/toolkit/components/url-classifier/nsUrlClassifierPrefixSet.cpp
@@ -35,56 +35,60 @@
  * 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 ***** */
 
 #include "nsAutoPtr.h"
 #include "nsCOMPtr.h"
+#include "nsDebug.h"
 #include "nsTArray.h"
 #include "nsUrlClassifierPrefixSet.h"
 #include "nsIUrlClassifierPrefixSet.h"
+#include "nsIFile.h"
+#include "nsILocalFile.h"
 #include "nsToolkitCompsCID.h"
 #include "nsTArray.h"
 #include "nsThreadUtils.h"
 #include "mozilla/Mutex.h"
+#include "mozilla/FileUtils.h"
 #include "prlog.h"
 
 using namespace mozilla;
 
 // NSPR_LOG_MODULES=UrlClassifierPrefixSet:5
 #if defined(PR_LOGGING)
 static const PRLogModuleInfo *gUrlClassifierPrefixSetLog = nsnull;
 #define LOG(args) PR_LOG(gUrlClassifierPrefixSetLog, PR_LOG_DEBUG, args)
 #define LOG_ENABLED() PR_LOG_TEST(gUrlClassifierPrefixSetLog, 4)
 #else
 #define LOG(args)
 #define LOG_ENABLED() (PR_FALSE)
 #endif
 
-NS_IMPL_THREADSAFE_ISUPPORTS1(nsUrlClassifierPrefixSet, nsIUrlClassifierPrefixSet);
+NS_IMPL_THREADSAFE_ISUPPORTS1(nsUrlClassifierPrefixSet, nsIUrlClassifierPrefixSet)
 
 nsUrlClassifierPrefixSet::nsUrlClassifierPrefixSet()
-  : mPrefixTreeLock("mPrefixTreeLock"),
-    mTreeIsReady(mPrefixTreeLock, "mTreeIsReady"),
+  : mPrefixSetLock("mPrefixSetLock"),
+    mSetIsReady(mPrefixSetLock, "mSetIsReady"),
     mHasPrefixes(PR_FALSE)
 {
 #if defined(PR_LOGGING)
   if (!gUrlClassifierPrefixSetLog)
     gUrlClassifierPrefixSetLog = PR_NewLogModule("UrlClassifierPrefixSet");
 #endif
   LOG(("Instantiating PrefixSet"));
 }
 
 NS_IMETHODIMP
 nsUrlClassifierPrefixSet::SetPrefixes(const PRUint32 * aArray, PRUint32 aLength)
 {
   {
-    MutexAutoLock lock(mPrefixTreeLock);
+    MutexAutoLock lock(mPrefixSetLock);
     if (mHasPrefixes) {
       LOG(("Clearing PrefixSet"));
       mDeltas.Clear();
       mIndexPrefixes.Clear();
       mIndexStarts.Clear();
       mHasPrefixes = PR_FALSE;
     }
   }
@@ -131,25 +135,25 @@ nsUrlClassifierPrefixSet::AddPrefixes(co
 
   mNewIndexPrefixes.Compact();
   mNewIndexStarts.Compact();
   mNewDeltas.Compact();
 
   LOG(("Total number of indices: %d", mNewIndexPrefixes.Length()));
   LOG(("Total number of deltas: %d", mNewDeltas.Length()));
 
-  MutexAutoLock lock(mPrefixTreeLock);
+  MutexAutoLock lock(mPrefixSetLock);
 
   // This just swaps some pointers
   mIndexPrefixes.SwapElements(mNewIndexPrefixes);
   mIndexStarts.SwapElements(mNewIndexStarts);
   mDeltas.SwapElements(mNewDeltas);
 
   mHasPrefixes = PR_TRUE;
-  mTreeIsReady.NotifyAll();
+  mSetIsReady.NotifyAll();
 
   return NS_OK;
 }
 
 PRUint32 nsUrlClassifierPrefixSet::BinSearch(PRUint32 start,
                                              PRUint32 end,
                                              PRUint32 target)
 {
@@ -212,44 +216,175 @@ nsUrlClassifierPrefixSet::Contains(PRUin
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsUrlClassifierPrefixSet::EstimateSize(PRUint32 * aSize)
 {
-  MutexAutoLock lock(mPrefixTreeLock);
+  MutexAutoLock lock(mPrefixSetLock);
   *aSize = sizeof(PRBool);
   if (mHasPrefixes) {
     *aSize += sizeof(PRUint16) * mDeltas.Length();
     *aSize += sizeof(PRUint32) * mIndexPrefixes.Length();
     *aSize += sizeof(PRUint32) * mIndexStarts.Length();
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsUrlClassifierPrefixSet::IsEmpty(PRBool * aEmpty)
+{
+  MutexAutoLock lock(mPrefixSetLock);
+  *aEmpty = !mHasPrefixes;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsUrlClassifierPrefixSet::Probe(PRUint32 aPrefix, PRBool * aReady, PRBool * aFound)
 {
-  MutexAutoLock lock(mPrefixTreeLock);
+  MutexAutoLock lock(mPrefixSetLock);
 
   // check whether we are opportunistically probing or should wait
   if (*aReady) {
     // we should block until we are ready
     while (!mHasPrefixes) {
-      LOG(("Tree is empty, probe must wait"));
-      mTreeIsReady.Wait();
+      LOG(("Set is empty, probe must wait"));
+      mSetIsReady.Wait();
     }
   } else {
     // opportunistic probe -> check if set is loaded
     if (mHasPrefixes) {
       *aReady = PR_TRUE;
     } else {
       return NS_OK;
     }
   }
 
   nsresult rv = Contains(aPrefix, aFound);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
+
+nsresult
+nsUrlClassifierPrefixSet::LoadFromFd(AutoFDClose & fileFd)
+{
+  PRUint32 magic;
+  PRInt32 read;
+
+  read = PR_Read(fileFd, &magic, sizeof(PRUint32));
+  NS_ENSURE_TRUE(read > 0, NS_ERROR_FAILURE);
+
+  if (magic == PREFIXSET_VERSION_MAGIC) {
+    PRUint32 indexSize;
+    PRUint32 deltaSize;
+
+    read = PR_Read(fileFd, &indexSize, sizeof(PRUint32));
+    NS_ENSURE_TRUE(read > 0, NS_ERROR_FAILURE);
+    read = PR_Read(fileFd, &deltaSize, sizeof(PRUint32));
+    NS_ENSURE_TRUE(read > 0, NS_ERROR_FAILURE);
+
+    if (indexSize == 0) {
+      LOG(("stored PrefixSet is empty!"));
+      return NS_ERROR_FAILURE;
+    }
+
+    nsTArray<PRUint32> mNewIndexPrefixes;
+    nsTArray<PRUint32> mNewIndexStarts;
+    nsTArray<PRUint16> mNewDeltas;
+
+    mNewIndexStarts.SetLength(indexSize);
+    mNewIndexPrefixes.SetLength(indexSize);
+    mNewDeltas.SetLength(deltaSize);
+
+    read = PR_Read(fileFd, mNewIndexPrefixes.Elements(), indexSize*sizeof(PRUint32));
+    NS_ENSURE_TRUE(read > 0, NS_ERROR_FAILURE);
+    read = PR_Read(fileFd, mNewIndexStarts.Elements(), indexSize*sizeof(PRUint32));
+    NS_ENSURE_TRUE(read > 0, NS_ERROR_FAILURE);
+    if (deltaSize > 0) {
+      read = PR_Read(fileFd, mNewDeltas.Elements(), deltaSize*sizeof(PRUint16));
+      NS_ENSURE_TRUE(read > 0, NS_ERROR_FAILURE);
+    }
+
+    MutexAutoLock lock(mPrefixSetLock);
+
+    mIndexPrefixes.SwapElements(mNewIndexPrefixes);
+    mIndexStarts.SwapElements(mNewIndexStarts);
+    mDeltas.SwapElements(mNewDeltas);
+
+    mHasPrefixes = PR_TRUE;
+    mSetIsReady.NotifyAll();
+  } else {
+    LOG(("Version magic mismatch, not loading"));
+    return NS_ERROR_FAILURE;
+  }
+
+  LOG(("Loading PrefixSet successful"));
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsUrlClassifierPrefixSet::LoadFromFile(nsIFile * aFile)
+{
+  nsresult rv;
+  nsCOMPtr<nsILocalFile> file(do_QueryInterface(aFile, &rv));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  AutoFDClose fileFd;
+  rv = file->OpenNSPRFileDesc(PR_RDONLY, 0, &fileFd);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return LoadFromFd(fileFd);
+}
+
+nsresult
+nsUrlClassifierPrefixSet::StoreToFd(AutoFDClose & fileFd)
+{
+  PRInt32 written;
+  PRUint32 magic = PREFIXSET_VERSION_MAGIC;
+  written = PR_Write(fileFd, &magic, sizeof(PRUint32));
+  NS_ENSURE_TRUE(written > 0, NS_ERROR_FAILURE);
+
+  PRUint32 indexSize = mIndexStarts.Length();
+  PRUint32 deltaSize = mDeltas.Length();
+  written = PR_Write(fileFd, &indexSize, sizeof(PRUint32));
+  NS_ENSURE_TRUE(written > 0, NS_ERROR_FAILURE);
+  written = PR_Write(fileFd, &deltaSize, sizeof(PRUint32));
+  NS_ENSURE_TRUE(written > 0, NS_ERROR_FAILURE);
+
+  written = PR_Write(fileFd, mIndexPrefixes.Elements(), indexSize * sizeof(PRUint32));
+  NS_ENSURE_TRUE(written > 0, NS_ERROR_FAILURE);
+  written = PR_Write(fileFd, mIndexStarts.Elements(), indexSize * sizeof(PRUint32));
+  NS_ENSURE_TRUE(written > 0, NS_ERROR_FAILURE);
+  if (deltaSize > 0) {
+    written = PR_Write(fileFd, mDeltas.Elements(), deltaSize * sizeof(PRUint16));
+    NS_ENSURE_TRUE(written > 0, NS_ERROR_FAILURE);
+  }
+
+  LOG(("Saving PrefixSet successful\n"));
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsUrlClassifierPrefixSet::StoreToFile(nsIFile * aFile)
+{
+  if (!mHasPrefixes) {
+    LOG(("Attempt to serialize empty PrefixSet"));
+    return NS_ERROR_FAILURE;
+  }
+
+  nsresult rv;
+  nsCOMPtr<nsILocalFile> file(do_QueryInterface(aFile, &rv));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  AutoFDClose fileFd;
+  rv = file->OpenNSPRFileDesc(PR_RDWR | PR_TRUNCATE | PR_CREATE_FILE,
+                              0644, &fileFd);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  MutexAutoLock lock(mPrefixSetLock);
+
+  return StoreToFd(fileFd);
+}
--- a/toolkit/components/url-classifier/nsUrlClassifierPrefixSet.h
+++ b/toolkit/components/url-classifier/nsUrlClassifierPrefixSet.h
@@ -38,50 +38,58 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef nsUrlClassifierPrefixSet_h_
 #define nsUrlClassifierPrefixSet_h_
 
 #include "nsISupportsUtils.h"
 #include "nsID.h"
+#include "nsIFile.h"
 #include "nsIUrlClassifierPrefixSet.h"
 #include "nsToolkitCompsCID.h"
 #include "mozilla/Mutex.h"
 #include "mozilla/CondVar.h"
+#include "mozilla/FileUtils.h"
 
 class nsUrlClassifierPrefixSet : public nsIUrlClassifierPrefixSet
 {
 public:
   nsUrlClassifierPrefixSet();
   virtual ~nsUrlClassifierPrefixSet() {};
 
   // Can send an empty Array to clean the tree
   NS_IMETHOD SetPrefixes(const PRUint32* aArray, PRUint32 aLength);
   // Given prefixes must be in sorted order and bigger than
-  // anything currently in the Prefix Tree
+  // anything currently in the Prefix Set
   NS_IMETHOD AddPrefixes(const PRUint32* aArray, PRUint32 aLength);
   // Does the PrefixSet contain this prefix? not thread-safe
   NS_IMETHOD Contains(PRUint32 aPrefix, PRBool* aFound);
   // Do a lookup in the PrefixSet
   // if aReady is set, we will block until there are any entries
   // if not set, we will return in aReady whether we were ready or not
   NS_IMETHOD Probe(PRUint32 aPrefix, PRBool* aReady, PRBool* aFound);
   NS_IMETHOD EstimateSize(PRUint32* aSize);
+  NS_IMETHOD IsEmpty(PRBool * aEmpty);
+  NS_IMETHOD LoadFromFile(nsIFile * aFile);
+  NS_IMETHOD StoreToFile(nsIFile * aFile);
 
   NS_DECL_ISUPPORTS
 
 protected:
   static const PRUint32 DELTAS_LIMIT = 100;
   static const PRUint32 MAX_INDEX_DIFF = (1 << 16);
+  static const PRUint32 PREFIXSET_VERSION_MAGIC = 1;
 
-  mozilla::Mutex mPrefixTreeLock;
-  mozilla::CondVar mTreeIsReady;
+  mozilla::Mutex mPrefixSetLock;
+  mozilla::CondVar mSetIsReady;
 
   PRUint32 BinSearch(PRUint32 start, PRUint32 end, PRUint32 target);
+  nsresult LoadFromFd(mozilla::AutoFDClose & fileFd);
+  nsresult StoreToFd(mozilla::AutoFDClose & fileFd);
 
   // boolean indicating whether |setPrefixes| has been
   // called with a non-empty array.
   PRBool mHasPrefixes;
   // the prefix for each index.
   nsTArray<PRUint32> mIndexPrefixes;
   // the value corresponds to the beginning of the run
   // (an index in |_deltas|) for the index