Bug 1353956 - P3. Separate file processing and prefix data processing for SafeBrowsing prefix set. r=gcp
authorDimi Lee <dlee@mozilla.com>
Thu, 07 Mar 2019 14:40:56 +0000
changeset 520890 e083106dc24f6f562eb2d72aa02871a27a681ebe
parent 520889 c7a253aed4508a8df4f1309d889dbfd669cd1693
child 520891 3b5da75b9c7b7912e31007b9bac31b4a1ff4a558
push id10862
push userffxbld-merge
push dateMon, 11 Mar 2019 13:01:11 +0000
treeherdermozilla-beta@a2e7f5c935da [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgcp
bugs1353956
milestone67.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 1353956 - P3. Separate file processing and prefix data processing for SafeBrowsing prefix set. r=gcp SafeBrowsing prefix files LOAD/SAVE operations are handled in xxxPrefixSet.cpp. It would be more clear if xxxPrefixSet.cpp only processes prefix data, while LookupCacheV2/LookupCacheV4 which use prefix set process file. This patch doesn't change any behavior, testcases need to update because the LookupCache & xxxPrefixSet APIs are changed. Differential Revision: https://phabricator.services.mozilla.com/D21462
toolkit/components/url-classifier/LookupCache.cpp
toolkit/components/url-classifier/LookupCache.h
toolkit/components/url-classifier/LookupCacheV4.cpp
toolkit/components/url-classifier/LookupCacheV4.h
toolkit/components/url-classifier/VariableLengthPrefixSet.cpp
toolkit/components/url-classifier/VariableLengthPrefixSet.h
toolkit/components/url-classifier/nsIUrlClassifierPrefixSet.idl
toolkit/components/url-classifier/nsUrlClassifierPrefixSet.cpp
toolkit/components/url-classifier/nsUrlClassifierPrefixSet.h
toolkit/components/url-classifier/tests/gtest/TestUrlClassifierTableUpdateV4.cpp
toolkit/components/url-classifier/tests/gtest/TestVariableLengthPrefixSet.cpp
toolkit/components/url-classifier/tests/unit/test_prefixset.js
--- a/toolkit/components/url-classifier/LookupCache.cpp
+++ b/toolkit/components/url-classifier/LookupCache.cpp
@@ -41,16 +41,18 @@ extern mozilla::LazyLogModule gUrlClassi
 #define LOG(args) \
   MOZ_LOG(gUrlClassifierDbServiceLog, mozilla::LogLevel::Debug, args)
 #define LOG_ENABLED() \
   MOZ_LOG_TEST(gUrlClassifierDbServiceLog, mozilla::LogLevel::Debug)
 
 namespace mozilla {
 namespace safebrowsing {
 
+const uint32_t LookupCache::MAX_BUFFER_SIZE = 64 * 1024;
+
 const int CacheResultV2::VER = CacheResult::V2;
 const int CacheResultV4::VER = CacheResult::V4;
 
 const int LookupCacheV2::VER = 2;
 
 static void CStringToHexString(const nsACString& aIn, nsACString& aOut) {
   static const char* const lut = "0123456789ABCDEF";
 
@@ -646,21 +648,83 @@ nsresult LookupCacheV2::ReadCompletions(
   return NS_OK;
 }
 
 nsresult LookupCacheV2::ClearPrefixes() {
   return mPrefixSet->SetPrefixes(nullptr, 0);
 }
 
 nsresult LookupCacheV2::StoreToFile(nsCOMPtr<nsIFile>& aFile) {
-  return mPrefixSet->StoreToFile(aFile);
+  nsCOMPtr<nsIOutputStream> localOutFile;
+  nsresult rv =
+      NS_NewLocalFileOutputStream(getter_AddRefs(localOutFile), aFile,
+                                  PR_WRONLY | PR_TRUNCATE | PR_CREATE_FILE);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  uint32_t fileSize;
+
+  // Preallocate the file storage
+  {
+    nsCOMPtr<nsIFileOutputStream> fos(do_QueryInterface(localOutFile));
+    Telemetry::AutoTimer<Telemetry::URLCLASSIFIER_PS_FALLOCATE_TIME> timer;
+
+    fileSize = mPrefixSet->CalculatePreallocateSize();
+
+    // Ignore failure, the preallocation is a hint and we write out the entire
+    // file later on
+    Unused << fos->Preallocate(fileSize);
+  }
+
+  // Convert to buffered stream
+  nsCOMPtr<nsIOutputStream> out;
+  rv = NS_NewBufferedOutputStream(getter_AddRefs(out), localOutFile.forget(),
+                                  std::min(fileSize, MAX_BUFFER_SIZE));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = mPrefixSet->WritePrefixes(out);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  LOG(("[%s] Storing PrefixSet successful", mTableName.get()));
+  return NS_OK;
 }
 
 nsresult LookupCacheV2::LoadFromFile(nsCOMPtr<nsIFile>& aFile) {
-  return mPrefixSet->LoadFromFile(aFile);
+  Telemetry::AutoTimer<Telemetry::URLCLASSIFIER_PS_FILELOAD_TIME> timer;
+
+  nsCOMPtr<nsIInputStream> localInFile;
+  nsresult rv = NS_NewLocalFileInputStream(getter_AddRefs(localInFile), aFile,
+                                           PR_RDONLY | nsIFile::OS_READAHEAD);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Calculate how big the file is, make sure our read buffer isn't bigger
+  // than the file itself which is just wasting memory.
+  int64_t fileSize;
+  rv = aFile->GetFileSize(&fileSize);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (fileSize < 0 || fileSize > UINT32_MAX) {
+    return NS_ERROR_FAILURE;
+  }
+
+  uint32_t bufferSize =
+      std::min<uint32_t>(static_cast<uint32_t>(fileSize), MAX_BUFFER_SIZE);
+
+  // Convert to buffered stream
+  nsCOMPtr<nsIInputStream> in;
+  rv = NS_NewBufferedInputStream(getter_AddRefs(in), localInFile.forget(),
+                                 bufferSize);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = mPrefixSet->LoadPrefixes(in);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  mPrimed = true;
+  LOG(("[%s] Loading PrefixSet successful", mTableName.get()));
+
+  return NS_OK;
 }
 
 size_t LookupCacheV2::SizeOfPrefixSet() const {
   return mPrefixSet->SizeOfIncludingThis(moz_malloc_size_of);
 }
 
 #ifdef DEBUG
 template <class T>
--- a/toolkit/components/url-classifier/LookupCache.h
+++ b/toolkit/components/url-classifier/LookupCache.h
@@ -226,16 +226,19 @@ class LookupCache {
   void GetCacheInfo(nsIUrlClassifierCacheInfo** aCache) const;
 
   virtual nsresult Open();
   virtual nsresult Init() = 0;
   virtual nsresult ClearPrefixes() = 0;
   virtual nsresult Has(const Completion& aCompletion, bool* aHas,
                        uint32_t* aMatchLength, bool* aConfirmed) = 0;
 
+  virtual nsresult StoreToFile(nsCOMPtr<nsIFile>& aFile) = 0;
+  virtual nsresult LoadFromFile(nsCOMPtr<nsIFile>& aFile) = 0;
+
   virtual bool IsEmpty() const = 0;
 
   virtual void ClearAll();
 
   template <typename T>
   static T* Cast(LookupCache* aThat) {
     return ((aThat && T::VER == aThat->Ver()) ? reinterpret_cast<T*>(aThat)
                                               : nullptr);
@@ -245,25 +248,25 @@ class LookupCache {
     return ((aThat && T::VER == aThat->Ver())
                 ? reinterpret_cast<const T*>(aThat)
                 : nullptr);
   }
 
  private:
   nsresult LoadPrefixSet();
 
-  virtual nsresult StoreToFile(nsCOMPtr<nsIFile>& aFile) = 0;
-  virtual nsresult LoadFromFile(nsCOMPtr<nsIFile>& aFile) = 0;
   virtual size_t SizeOfPrefixSet() const = 0;
 
   virtual int Ver() const = 0;
 
  protected:
   virtual ~LookupCache() {}
 
+  static const uint32_t MAX_BUFFER_SIZE;
+
   // Check completions in positive cache and prefix in negative cache.
   // 'aHas' and 'aConfirmed' are output parameters.
   nsresult CheckCache(const Completion& aCompletion, bool* aHas,
                       bool* aConfirmed);
 
   bool mPrimed;  // true when the PrefixSet has been loaded (or constructed)
   const nsCString mTableName;
   const nsCString mProvider;
@@ -287,16 +290,19 @@ class LookupCacheV2 final : public Looku
       : LookupCache(aTableName, aProvider, aStoreFile) {}
 
   virtual nsresult Init() override;
   virtual nsresult Open() override;
   virtual void ClearAll() override;
   virtual nsresult Has(const Completion& aCompletion, bool* aHas,
                        uint32_t* aMatchLength, bool* aConfirmed) override;
 
+  virtual nsresult StoreToFile(nsCOMPtr<nsIFile>& aFile) override;
+  virtual nsresult LoadFromFile(nsCOMPtr<nsIFile>& aFile) override;
+
   virtual bool IsEmpty() const override;
 
   nsresult Build(AddPrefixArray& aAddPrefixes, AddCompleteArray& aAddCompletes);
 
   nsresult GetPrefixes(FallibleTArray<uint32_t>& aAddPrefixes);
 
   // This will Clear() the passed arrays when done.
   // 'aExpirySec' is used by testcase to config an expired time.
@@ -309,18 +315,16 @@ class LookupCacheV2 final : public Looku
 #endif
 
   static const int VER;
 
  protected:
   nsresult ReadCompletions();
 
   virtual nsresult ClearPrefixes() override;
-  virtual nsresult StoreToFile(nsCOMPtr<nsIFile>& aFile) override;
-  virtual nsresult LoadFromFile(nsCOMPtr<nsIFile>& aFile) override;
   virtual size_t SizeOfPrefixSet() const override;
 
  private:
   ~LookupCacheV2() {}
 
   virtual int Ver() const override { return VER; }
 
   // Construct a Prefix Set with known prefixes.
--- a/toolkit/components/url-classifier/LookupCacheV4.cpp
+++ b/toolkit/components/url-classifier/LookupCacheV4.cpp
@@ -159,21 +159,84 @@ nsresult LookupCacheV4::GetFixedLengthPr
 
 nsresult LookupCacheV4::ClearPrefixes() {
   // Clear by seting a empty map
   PrefixStringMap map;
   return mVLPrefixSet->SetPrefixes(map);
 }
 
 nsresult LookupCacheV4::StoreToFile(nsCOMPtr<nsIFile>& aFile) {
-  return mVLPrefixSet->StoreToFile(aFile);
+  NS_ENSURE_ARG_POINTER(aFile);
+
+  nsCOMPtr<nsIOutputStream> localOutFile;
+  nsresult rv =
+      NS_NewLocalFileOutputStream(getter_AddRefs(localOutFile), aFile,
+                                  PR_WRONLY | PR_TRUNCATE | PR_CREATE_FILE);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  uint32_t fileSize = 0;
+  // Preallocate the file storage
+  {
+    nsCOMPtr<nsIFileOutputStream> fos(do_QueryInterface(localOutFile));
+    Telemetry::AutoTimer<Telemetry::URLCLASSIFIER_VLPS_FALLOCATE_TIME> timer;
+
+    fileSize = mVLPrefixSet->CalculatePreallocateSize();
+
+    Unused << fos->Preallocate(fileSize);
+  }
+
+  // Convert to buffered stream
+  nsCOMPtr<nsIOutputStream> out;
+  rv = NS_NewBufferedOutputStream(getter_AddRefs(out), localOutFile.forget(),
+                                  std::min(fileSize, MAX_BUFFER_SIZE));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = mVLPrefixSet->WritePrefixes(out);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  LOG(("[%s] Storing PrefixSet successful", mTableName.get()));
+  return NS_OK;
 }
 
 nsresult LookupCacheV4::LoadFromFile(nsCOMPtr<nsIFile>& aFile) {
-  return mVLPrefixSet->LoadFromFile(aFile);
+  Telemetry::AutoTimer<Telemetry::URLCLASSIFIER_VLPS_FILELOAD_TIME> timer;
+
+  nsCOMPtr<nsIInputStream> localInFile;
+  nsresult rv = NS_NewLocalFileInputStream(getter_AddRefs(localInFile), aFile,
+                                           PR_RDONLY | nsIFile::OS_READAHEAD);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Calculate how big the file is, make sure our read buffer isn't bigger
+  // than the file itself which is just wasting memory.
+  int64_t fileSize;
+  rv = aFile->GetFileSize(&fileSize);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (fileSize < 0 || fileSize > UINT32_MAX) {
+    return NS_ERROR_FAILURE;
+  }
+
+  uint32_t bufferSize =
+      std::min<uint32_t>(static_cast<uint32_t>(fileSize), MAX_BUFFER_SIZE);
+
+  // Convert to buffered stream
+  nsCOMPtr<nsIInputStream> in;
+  rv = NS_NewBufferedInputStream(getter_AddRefs(in), localInFile.forget(),
+                                 bufferSize);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = mVLPrefixSet->LoadPrefixes(in);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  mPrimed = true;
+  LOG(("[%s] Loading PrefixSet successful", mTableName.get()));
+
+  return NS_OK;
 }
 
 size_t LookupCacheV4::SizeOfPrefixSet() const {
   return mVLPrefixSet->SizeOfIncludingThis(moz_malloc_size_of);
 }
 
 static nsresult AppendPrefixToMap(PrefixStringMap& prefixes,
                                   const nsACString& prefix) {
--- a/toolkit/components/url-classifier/LookupCacheV4.h
+++ b/toolkit/components/url-classifier/LookupCacheV4.h
@@ -20,16 +20,19 @@ class LookupCacheV4 final : public Looku
                          const nsACString& aProvider,
                          nsCOMPtr<nsIFile>& aStoreFile)
       : LookupCache(aTableName, aProvider, aStoreFile) {}
 
   virtual nsresult Init() override;
   virtual nsresult Has(const Completion& aCompletion, bool* aHas,
                        uint32_t* aMatchLength, bool* aConfirmed) override;
 
+  virtual nsresult StoreToFile(nsCOMPtr<nsIFile>& aFile) override;
+  virtual nsresult LoadFromFile(nsCOMPtr<nsIFile>& aFile) override;
+
   virtual bool IsEmpty() const override;
 
   nsresult Build(PrefixStringMap& aPrefixMap);
 
   nsresult GetPrefixes(PrefixStringMap& aPrefixMap);
   nsresult GetFixedLengthPrefixes(FallibleTArray<uint32_t>& aPrefixes);
 
   // ApplyUpdate will merge data stored in aTableUpdate with prefixes in
@@ -42,18 +45,16 @@ class LookupCacheV4 final : public Looku
   nsresult WriteMetadata(RefPtr<const TableUpdateV4> aTableUpdate);
   nsresult LoadMetadata(nsACString& aState, nsACString& aChecksum);
 
   static const int VER;
   static const uint32_t MAX_METADATA_VALUE_LENGTH;
 
  protected:
   virtual nsresult ClearPrefixes() override;
-  virtual nsresult StoreToFile(nsCOMPtr<nsIFile>& aFile) override;
-  virtual nsresult LoadFromFile(nsCOMPtr<nsIFile>& aFile) override;
   virtual size_t SizeOfPrefixSet() const override;
 
  private:
   ~LookupCacheV4() {}
 
   virtual int Ver() const override { return VER; }
 
   RefPtr<VariableLengthPrefixSet> mVLPrefixSet;
--- a/toolkit/components/url-classifier/VariableLengthPrefixSet.cpp
+++ b/toolkit/components/url-classifier/VariableLengthPrefixSet.cpp
@@ -23,19 +23,16 @@ static mozilla::LazyLogModule gUrlClassi
 
 namespace mozilla {
 namespace safebrowsing {
 
 #define PREFIX_SIZE_FIXED 4
 
 NS_IMPL_ISUPPORTS(VariableLengthPrefixSet, nsIMemoryReporter)
 
-// Definition required due to std::max<>()
-const uint32_t VariableLengthPrefixSet::MAX_BUFFER_SIZE;
-
 // This class will process prefix size between 4~32. But for 4 bytes prefixes,
 // they will be passed to nsUrlClassifierPrefixSet because of better
 // optimization.
 VariableLengthPrefixSet::VariableLengthPrefixSet()
     : mLock("VariableLengthPrefixSet.mLock"),
       mFixedPrefixSet(new nsUrlClassifierPrefixSet) {}
 
 nsresult VariableLengthPrefixSet::Init(const nsACString& aName) {
@@ -199,101 +196,28 @@ nsresult VariableLengthPrefixSet::IsEmpt
   NS_ENSURE_ARG_POINTER(aEmpty);
 
   mFixedPrefixSet->IsEmpty(aEmpty);
   *aEmpty = *aEmpty && mVLPrefixSet.IsEmpty();
 
   return NS_OK;
 }
 
-nsresult VariableLengthPrefixSet::LoadFromFile(nsCOMPtr<nsIFile>& aFile) {
+nsresult VariableLengthPrefixSet::LoadPrefixes(nsCOMPtr<nsIInputStream>& in) {
   MutexAutoLock lock(mLock);
 
-  NS_ENSURE_ARG_POINTER(aFile);
-
-  Telemetry::AutoTimer<Telemetry::URLCLASSIFIER_VLPS_FILELOAD_TIME> timer;
-
-  nsCOMPtr<nsIInputStream> localInFile;
-  nsresult rv = NS_NewLocalFileInputStream(getter_AddRefs(localInFile), aFile,
-                                           PR_RDONLY | nsIFile::OS_READAHEAD);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // Calculate how big the file is, make sure our read buffer isn't bigger
-  // than the file itself which is just wasting memory.
-  int64_t fileSize;
-  rv = aFile->GetFileSize(&fileSize);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  if (fileSize < 0 || fileSize > UINT32_MAX) {
-    return NS_ERROR_FAILURE;
-  }
-
-  uint32_t bufferSize =
-      std::min<uint32_t>(static_cast<uint32_t>(fileSize), MAX_BUFFER_SIZE);
-
-  // Convert to buffered stream
-  nsCOMPtr<nsIInputStream> in;
-  rv = NS_NewBufferedInputStream(getter_AddRefs(in), localInFile.forget(),
-                                 bufferSize);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = mFixedPrefixSet->LoadPrefixes(in);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = LoadPrefixes(in);
+  // First read prefixes from fixed-length prefix set
+  nsresult rv = mFixedPrefixSet->LoadPrefixes(in);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  return NS_OK;
-  ;
-}
-
-nsresult VariableLengthPrefixSet::StoreToFile(nsCOMPtr<nsIFile>& aFile) const {
-  NS_ENSURE_ARG_POINTER(aFile);
-
-  MutexAutoLock lock(mLock);
-
-  nsCOMPtr<nsIOutputStream> localOutFile;
-  nsresult rv =
-      NS_NewLocalFileOutputStream(getter_AddRefs(localOutFile), aFile,
-                                  PR_WRONLY | PR_TRUNCATE | PR_CREATE_FILE);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  uint32_t fileSize = 0;
-  // Preallocate the file storage
-  {
-    nsCOMPtr<nsIFileOutputStream> fos(do_QueryInterface(localOutFile));
-    Telemetry::AutoTimer<Telemetry::URLCLASSIFIER_VLPS_FALLOCATE_TIME> timer;
-
-    fileSize += mFixedPrefixSet->CalculatePreallocateSize();
-    fileSize += CalculatePreallocateSize();
-
-    Unused << fos->Preallocate(fileSize);
-  }
-
-  // Convert to buffered stream
-  nsCOMPtr<nsIOutputStream> out;
-  rv = NS_NewBufferedOutputStream(getter_AddRefs(out), localOutFile.forget(),
-                                  std::min(fileSize, MAX_BUFFER_SIZE));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = mFixedPrefixSet->WritePrefixes(out);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = WritePrefixes(out);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return NS_OK;
-}
-
-nsresult VariableLengthPrefixSet::LoadPrefixes(nsCOMPtr<nsIInputStream>& in) {
+  // Then read prefixes from variable-length prefix set
   uint32_t magic;
   uint32_t read;
 
-  nsresult rv =
-      in->Read(reinterpret_cast<char*>(&magic), sizeof(uint32_t), &read);
+  rv = in->Read(reinterpret_cast<char*>(&magic), sizeof(uint32_t), &read);
   NS_ENSURE_SUCCESS(rv, rv);
   NS_ENSURE_TRUE(read == sizeof(uint32_t), NS_ERROR_FAILURE);
 
   if (magic != PREFIXSET_VERSION_MAGIC) {
     LOG(("[%s] Version magic mismatch, not loading", mName.get()));
     return NS_ERROR_FILE_CORRUPTED;
   }
 
@@ -344,34 +268,45 @@ nsresult VariableLengthPrefixSet::LoadPr
        totalPrefixes));
 
   return NS_OK;
 }
 
 uint32_t VariableLengthPrefixSet::CalculatePreallocateSize() const {
   uint32_t fileSize = 0;
 
+  // Size of fixed length prefix set.
+  fileSize += mFixedPrefixSet->CalculatePreallocateSize();
+
+  // Size of variable length prefix set.
   // Store how many prefix string.
   fileSize += sizeof(uint32_t);
 
   for (auto iter = mVLPrefixSet.ConstIter(); !iter.Done(); iter.Next()) {
     // Store prefix size, prefix string length, and prefix string.
     fileSize += sizeof(uint8_t);
     fileSize += sizeof(uint32_t);
     fileSize += iter.Data()->Length();
   }
   return fileSize;
 }
 
 nsresult VariableLengthPrefixSet::WritePrefixes(
     nsCOMPtr<nsIOutputStream>& out) const {
+  MutexAutoLock lock(mLock);
+
+  // First, write fixed length prefix set
+  nsresult rv = mFixedPrefixSet->WritePrefixes(out);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Then, write variable length prefix set
   uint32_t written;
   uint32_t writelen = sizeof(uint32_t);
   uint32_t magic = PREFIXSET_VERSION_MAGIC;
-  nsresult rv = out->Write(reinterpret_cast<char*>(&magic), writelen, &written);
+  rv = out->Write(reinterpret_cast<char*>(&magic), writelen, &written);
   NS_ENSURE_SUCCESS(rv, rv);
   NS_ENSURE_TRUE(written == writelen, NS_ERROR_FAILURE);
 
   uint32_t count = mVLPrefixSet.Count();
   rv = out->Write(reinterpret_cast<char*>(&count), writelen, &written);
   NS_ENSURE_SUCCESS(rv, rv);
   NS_ENSURE_TRUE(written == writelen, NS_ERROR_FAILURE);
 
--- a/toolkit/components/url-classifier/VariableLengthPrefixSet.h
+++ b/toolkit/components/url-classifier/VariableLengthPrefixSet.h
@@ -25,37 +25,34 @@ class VariableLengthPrefixSet final : pu
 
   nsresult Init(const nsACString& aName);
   nsresult SetPrefixes(
       const mozilla::safebrowsing::PrefixStringMap& aPrefixMap);
   nsresult GetPrefixes(mozilla::safebrowsing::PrefixStringMap& aPrefixMap);
   nsresult GetFixedLengthPrefixes(FallibleTArray<uint32_t>& aPrefixes);
   nsresult Matches(const nsACString& aFullHash, uint32_t* aLength) const;
   nsresult IsEmpty(bool* aEmpty) const;
-  nsresult LoadFromFile(nsCOMPtr<nsIFile>& aFile);
-  nsresult StoreToFile(nsCOMPtr<nsIFile>& aFile) const;
+
+  nsresult WritePrefixes(nsCOMPtr<nsIOutputStream>& out) const;
+  nsresult LoadPrefixes(nsCOMPtr<nsIInputStream>& in);
+  uint32_t CalculatePreallocateSize() const;
 
   size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
 
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIMEMORYREPORTER
 
  private:
   virtual ~VariableLengthPrefixSet();
 
-  static const uint32_t MAX_BUFFER_SIZE = 64 * 1024;
   static const uint32_t PREFIXSET_VERSION_MAGIC = 1;
 
   bool BinarySearch(const nsACString& aFullHash, const nsACString& aPrefixes,
                     uint32_t aPrefixSize) const;
 
-  uint32_t CalculatePreallocateSize() const;
-  nsresult WritePrefixes(nsCOMPtr<nsIOutputStream>& out) const;
-  nsresult LoadPrefixes(nsCOMPtr<nsIInputStream>& in);
-
   // Lock to prevent races between the url-classifier thread (which does most
   // of the operations) and the main thread (which does memory reporting).
   // It should be held for all operations between Init() and destruction that
   // touch this class's data members.
   mutable mozilla::Mutex mLock;
 
   const RefPtr<nsUrlClassifierPrefixSet> mFixedPrefixSet;
   mozilla::safebrowsing::PrefixStringMap mVLPrefixSet;
--- a/toolkit/components/url-classifier/nsIUrlClassifierPrefixSet.idl
+++ b/toolkit/components/url-classifier/nsIUrlClassifierPrefixSet.idl
@@ -19,11 +19,9 @@ interface nsIUrlClassifierPrefixSet : ns
   // Requires array to be sorted.
   void setPrefixes([const, array, size_is(aLength)] in unsigned long aPrefixes,
                    in unsigned long aLength);
   void getPrefixes(out unsigned long aCount,
                   [array, size_is(aCount), retval] out unsigned long aPrefixes);
   // Do a lookup in the PrefixSet, return whether the value is present.
   boolean contains(in unsigned long aPrefix);
   boolean isEmpty();
-  void loadFromFile(in nsIFile aFile);
-  void storeToFile(in nsIFile aFile);
 };
--- a/toolkit/components/url-classifier/nsUrlClassifierPrefixSet.cpp
+++ b/toolkit/components/url-classifier/nsUrlClassifierPrefixSet.cpp
@@ -7,45 +7,39 @@
 #include "nsUrlClassifierPrefixSet.h"
 #include "nsIUrlClassifierPrefixSet.h"
 #include "crc32c.h"
 #include "nsCOMPtr.h"
 #include "nsDebug.h"
 #include "nsPrintfCString.h"
 #include "nsTArray.h"
 #include "nsString.h"
-#include "nsIFile.h"
 #include "nsTArray.h"
 #include "nsThreadUtils.h"
 #include "nsNetUtil.h"
 #include "nsISeekableStream.h"
 #include "nsIBufferedStreams.h"
-#include "nsIFileStreams.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Telemetry.h"
-#include "mozilla/FileUtils.h"
 #include "mozilla/Logging.h"
 #include "mozilla/Unused.h"
 #include <algorithm>
 
 using namespace mozilla;
 
 // MOZ_LOG=UrlClassifierPrefixSet:5
 static LazyLogModule gUrlClassifierPrefixSetLog("UrlClassifierPrefixSet");
 #define LOG(args) \
   MOZ_LOG(gUrlClassifierPrefixSetLog, mozilla::LogLevel::Debug, args)
 #define LOG_ENABLED() \
   MOZ_LOG_TEST(gUrlClassifierPrefixSetLog, mozilla::LogLevel::Debug)
 
 NS_IMPL_ISUPPORTS(nsUrlClassifierPrefixSet, nsIUrlClassifierPrefixSet,
                   nsIMemoryReporter)
 
-// Definition required due to std::max<>()
-const uint32_t nsUrlClassifierPrefixSet::MAX_BUFFER_SIZE;
-
 template <typename T>
 static void CalculateTArrayChecksum(const nsTArray<T>& aArray,
                                     uint32_t* outChecksum) {
   *outChecksum = ~0;
 
   for (size_t i = 0; i < aArray.Length(); i++) {
     const T& element = aArray[i];
     const void* pointer = &element;
@@ -340,93 +334,20 @@ bool nsUrlClassifierPrefixSet::IsEmptyIn
 NS_IMETHODIMP
 nsUrlClassifierPrefixSet::IsEmpty(bool* aEmpty) {
   MutexAutoLock lock(mLock);
 
   *aEmpty = IsEmptyInternal();
   return NS_OK;
 }
 
-NS_IMETHODIMP
-nsUrlClassifierPrefixSet::LoadFromFile(nsIFile* aFile) {
+nsresult nsUrlClassifierPrefixSet::LoadPrefixes(nsCOMPtr<nsIInputStream>& in) {
   MutexAutoLock lock(mLock);
 
-  Telemetry::AutoTimer<Telemetry::URLCLASSIFIER_PS_FILELOAD_TIME> timer;
-
-  nsCOMPtr<nsIInputStream> localInFile;
-  nsresult rv = NS_NewLocalFileInputStream(getter_AddRefs(localInFile), aFile,
-                                           PR_RDONLY | nsIFile::OS_READAHEAD);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // Calculate how big the file is, make sure our read buffer isn't bigger
-  // than the file itself which is just wasting memory.
-  int64_t fileSize;
-  rv = aFile->GetFileSize(&fileSize);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  if (fileSize < 0 || fileSize > UINT32_MAX) {
-    return NS_ERROR_FAILURE;
-  }
-
-  uint32_t bufferSize =
-      std::min<uint32_t>(static_cast<uint32_t>(fileSize), MAX_BUFFER_SIZE);
-
-  // Convert to buffered stream
-  nsCOMPtr<nsIInputStream> in;
-  rv = NS_NewBufferedInputStream(getter_AddRefs(in), localInFile.forget(),
-                                 bufferSize);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = LoadPrefixes(in);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsUrlClassifierPrefixSet::StoreToFile(nsIFile* aFile) {
-  MutexAutoLock lock(mLock);
-
-  nsCOMPtr<nsIOutputStream> localOutFile;
-  nsresult rv =
-      NS_NewLocalFileOutputStream(getter_AddRefs(localOutFile), aFile,
-                                  PR_WRONLY | PR_TRUNCATE | PR_CREATE_FILE);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  uint32_t fileSize;
-
-  // Preallocate the file storage
-  {
-    nsCOMPtr<nsIFileOutputStream> fos(do_QueryInterface(localOutFile));
-    Telemetry::AutoTimer<Telemetry::URLCLASSIFIER_PS_FALLOCATE_TIME> timer;
-
-    fileSize = CalculatePreallocateSize();
-
-    // Ignore failure, the preallocation is a hint and we write out the entire
-    // file later on
-    Unused << fos->Preallocate(fileSize);
-  }
-
-  // Convert to buffered stream
-  nsCOMPtr<nsIOutputStream> out;
-  rv = NS_NewBufferedOutputStream(getter_AddRefs(out), localOutFile.forget(),
-                                  std::min(fileSize, MAX_BUFFER_SIZE));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = WritePrefixes(out);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  LOG(("[%s] Storing PrefixSet successful", mName.get()));
-
-  return NS_OK;
-}
-
-nsresult nsUrlClassifierPrefixSet::LoadPrefixes(nsCOMPtr<nsIInputStream>& in) {
   mCanary.Check();
-
   Clear();
 
   uint32_t magic;
   uint32_t read;
 
   nsresult rv =
       in->Read(reinterpret_cast<char*>(&magic), sizeof(uint32_t), &read);
   NS_ENSURE_SUCCESS(rv, rv);
@@ -513,16 +434,18 @@ uint32_t nsUrlClassifierPrefixSet::Calcu
   uint32_t deltas = mTotalPrefixes - mIndexPrefixes.Length();
   fileSize += 2 * mIndexPrefixes.Length() * sizeof(uint32_t);
   fileSize += deltas * sizeof(uint16_t);
   return fileSize;
 }
 
 nsresult nsUrlClassifierPrefixSet::WritePrefixes(
     nsCOMPtr<nsIOutputStream>& out) const {
+  MutexAutoLock lock(mLock);
+
   mCanary.Check();
 
   // In Bug 1362761, crashes happened while reading mIndexDeltas[i].
   // We suspect that this is due to memory corruption so to test this
   // hypothesis, we will crash the browser. Once we have established
   // memory corruption as the root cause, we can attempt to gracefully
   // handle this.
   uint32_t checksum;
--- a/toolkit/components/url-classifier/nsUrlClassifierPrefixSet.h
+++ b/toolkit/components/url-classifier/nsUrlClassifierPrefixSet.h
@@ -34,43 +34,40 @@ class nsUrlClassifierPrefixSet final : p
  public:
   nsUrlClassifierPrefixSet();
 
   NS_IMETHOD Init(const nsACString& aName) override;
   NS_IMETHOD SetPrefixes(const uint32_t* aArray, uint32_t aLength) override;
   NS_IMETHOD GetPrefixes(uint32_t* aCount, uint32_t** aPrefixes) override;
   NS_IMETHOD Contains(uint32_t aPrefix, bool* aFound) override;
   NS_IMETHOD IsEmpty(bool* aEmpty) override;
-  NS_IMETHOD LoadFromFile(nsIFile* aFile) override;
-  NS_IMETHOD StoreToFile(nsIFile* aFile) override;
 
   nsresult GetPrefixesNative(FallibleTArray<uint32_t>& outArray);
+  nsresult WritePrefixes(nsCOMPtr<nsIOutputStream>& out) const;
+  nsresult LoadPrefixes(nsCOMPtr<nsIInputStream>& in);
+  uint32_t CalculatePreallocateSize() const;
 
   size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
 
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIMEMORYREPORTER
 
   friend class mozilla::safebrowsing::VariableLengthPrefixSet;
 
  private:
   virtual ~nsUrlClassifierPrefixSet();
 
-  static const uint32_t MAX_BUFFER_SIZE = 64 * 1024;
   static const uint32_t DELTAS_LIMIT = 120;
   static const uint32_t MAX_INDEX_DIFF = (1 << 16);
   static const uint32_t PREFIXSET_VERSION_MAGIC = 1;
 
   void Clear();
   nsresult MakePrefixSet(const uint32_t* aArray, uint32_t aLength);
   uint32_t BinSearch(uint32_t start, uint32_t end, uint32_t target) const;
   bool IsEmptyInternal() const;
-  uint32_t CalculatePreallocateSize() const;
-  nsresult WritePrefixes(nsCOMPtr<nsIOutputStream>& out) const;
-  nsresult LoadPrefixes(nsCOMPtr<nsIInputStream>& in);
 
   // Lock to prevent races between the url-classifier thread (which does most
   // of the operations) and the main thread (which does memory reporting).
   // It should be held for all operations between Init() and destruction that
   // touch this class's data members.
   mutable mozilla::Mutex mLock;
   // list of fully stored prefixes, that also form the
   // start of a run of deltas in mIndexDeltas.
--- a/toolkit/components/url-classifier/tests/gtest/TestUrlClassifierTableUpdateV4.cpp
+++ b/toolkit/components/url-classifier/tests/gtest/TestUrlClassifierTableUpdateV4.cpp
@@ -138,26 +138,27 @@ static void GenerateUpdateData(bool full
 
   tableUpdates.AppendElement(tableUpdate);
 }
 
 static void VerifyPrefixSet(PrefixStringMap& expected) {
   // Verify the prefix set is written to disk.
   nsCOMPtr<nsIFile> file;
   NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(file));
+  file->AppendNative(GTEST_SAFEBROWSING_DIR);
 
-  file->AppendNative(GTEST_SAFEBROWSING_DIR);
+  RefPtr<LookupCacheV4> lookup =
+      new LookupCacheV4(GTEST_TABLE, NS_LITERAL_CSTRING("test"), file);
+  lookup->Init();
+
   file->AppendNative(GTEST_PREFIXFILE);
-
-  RefPtr<VariableLengthPrefixSet> load = new VariableLengthPrefixSet;
-  load->Init(GTEST_TABLE);
+  lookup->LoadFromFile(file);
 
   PrefixStringMap prefixesInFile;
-  load->LoadFromFile(file);
-  load->GetPrefixes(prefixesInFile);
+  lookup->GetPrefixes(prefixesInFile);
 
   for (auto iter = expected.ConstIter(); !iter.Done(); iter.Next()) {
     nsCString* expectedPrefix = iter.Data();
     nsCString* resultPrefix = prefixesInFile.Get(iter.Key());
 
     ASSERT_TRUE(*resultPrefix == *expectedPrefix);
   }
 }
@@ -228,33 +229,33 @@ static void testOpenLookupCache() {
     ASSERT_EQ(rv, NS_OK);
 
     rv = cache->Open();
     ASSERT_EQ(rv, NS_OK);
   });
 }
 
 // Tests start from here.
-TEST(UrlClassifierTableUpdateV4, FixLenghtPSetFullUpdate) {
+TEST(UrlClassifierTableUpdateV4, FixLengthPSetFullUpdate) {
   srand(time(NULL));
 
   _PrefixArray array;
   PrefixStringMap map;
   nsCString sha256;
 
   CreateRandomSortedPrefixArray(5000, 4, 4, array);
   PrefixArrayToPrefixStringMap(array, map);
   CalculateSHA256(array, sha256);
 
   testFullUpdate(map, &sha256);
 
   Clear();
 }
 
-TEST(UrlClassifierTableUpdateV4, VariableLenghtPSetFullUpdate) {
+TEST(UrlClassifierTableUpdateV4, VariableLengthPSetFullUpdate) {
   _PrefixArray array;
   PrefixStringMap map;
   nsCString sha256;
 
   CreateRandomSortedPrefixArray(5000, 5, 32, array);
   PrefixArrayToPrefixStringMap(array, map);
   CalculateSHA256(array, sha256);
 
--- a/toolkit/components/url-classifier/tests/gtest/TestVariableLengthPrefixSet.cpp
+++ b/toolkit/components/url-classifier/tests/gtest/TestVariableLengthPrefixSet.cpp
@@ -12,21 +12,21 @@
 #include "gtest/gtest.h"
 
 using namespace mozilla::safebrowsing;
 
 typedef nsCString _Prefix;
 typedef nsTArray<_Prefix> _PrefixArray;
 
 // Create fullhash by appending random characters.
-static nsCString* CreateFullHash(const nsACString& in) {
-  nsCString* out = new nsCString(in);
-  out->SetLength(32);
+static nsCString CreateFullHash(const nsACString& in) {
+  nsCString out(in);
+  out.SetLength(32);
   for (size_t i = in.Length(); i < 32; i++) {
-    out->SetCharAt(char(rand() % 256), i);
+    out.SetCharAt(char(rand() % 256), i);
   }
 
   return out;
 }
 
 // This function generate N prefixes with size between MIN and MAX.
 // The output array will not be cleared, random result will append to it
 static void RandomPrefixes(uint32_t N, uint32_t MIN, uint32_t MAX,
@@ -50,66 +50,65 @@ static void RandomPrefixes(uint32_t N, u
       if (!array.Contains(prefix)) {
         array.AppendElement(prefix);
         added = true;
       }
     }
   }
 }
 
-static void CheckContent(VariableLengthPrefixSet* pset,
-                         PrefixStringMap& expected) {
+static void CheckContent(LookupCacheV4* cache, PrefixStringMap& expected) {
   PrefixStringMap vlPSetMap;
-  pset->GetPrefixes(vlPSetMap);
+  cache->GetPrefixes(vlPSetMap);
 
   for (auto iter = vlPSetMap.Iter(); !iter.Done(); iter.Next()) {
     nsCString* expectedPrefix = expected.Get(iter.Key());
     nsCString* resultPrefix = iter.Data();
 
     ASSERT_TRUE(resultPrefix->Equals(*expectedPrefix));
   }
 }
 
 // This test loops through all the prefixes and converts each prefix to
 // fullhash by appending random characters, each converted fullhash
 // should at least match its original length in the prefixSet.
-static void DoExpectedLookup(VariableLengthPrefixSet* pset,
-                             _PrefixArray& array) {
+static void DoExpectedLookup(LookupCacheV4* cache, _PrefixArray& array) {
   uint32_t matchLength = 0;
   for (uint32_t i = 0; i < array.Length(); i++) {
     const nsCString& prefix = array[i];
-    UniquePtr<nsCString> fullhash(CreateFullHash(prefix));
+    Completion complete;
+    complete.Assign(CreateFullHash(prefix));
 
     // Find match for prefix-generated full hash
-    pset->Matches(*fullhash, &matchLength);
+    bool has, confirmed;
+    cache->Has(complete, &has, &matchLength, &confirmed);
     MOZ_ASSERT(matchLength != 0);
 
     if (matchLength != prefix.Length()) {
       // Return match size is not the same as prefix size.
       // In this case it could be because the generated fullhash match other
       // prefixes, check if this prefix exist.
       bool found = false;
 
       for (uint32_t j = 0; j < array.Length(); j++) {
         if (array[j].Length() != matchLength) {
           continue;
         }
 
-        if (0 == memcmp(fullhash->BeginReading(), array[j].BeginReading(),
-                        matchLength)) {
+        if (0 == memcmp(complete.buf, array[j].BeginReading(), matchLength)) {
           found = true;
           break;
         }
       }
       ASSERT_TRUE(found);
     }
   }
 }
 
-static void DoRandomLookup(VariableLengthPrefixSet* pset, uint32_t N,
+static void DoRandomLookup(LookupCacheV4* cache, uint32_t N,
                            _PrefixArray& array) {
   for (uint32_t i = 0; i < N; i++) {
     // Random 32-bytes test fullhash
     char buf[32];
     for (uint32_t j = 0; j < 32; j++) {
       buf[j] = (char)(rand() % 256);
     }
 
@@ -117,18 +116,21 @@ static void DoRandomLookup(VariableLengt
     nsTArray<uint32_t> expected;
     for (uint32_t j = 0; j < array.Length(); j++) {
       const nsACString& str = array[j];
       if (0 == memcmp(buf, str.BeginReading(), str.Length())) {
         expected.AppendElement(str.Length());
       }
     }
 
+    Completion complete;
+    complete.Assign(nsDependentCSubstring(buf, 32));
+    bool has, confirmed;
     uint32_t matchLength = 0;
-    pset->Matches(nsDependentCSubstring(buf, 32), &matchLength);
+    cache->Has(complete, &has, &matchLength, &confirmed);
 
     ASSERT_TRUE(expected.IsEmpty() ? !matchLength
                                    : expected.Contains(matchLength));
   }
 }
 
 static void SetupPrefixMap(const _PrefixArray& array, PrefixStringMap& map) {
   map.Clear();
@@ -163,99 +165,100 @@ static void SetupPrefixMap(const _Prefix
       memcpy(dst, iter.Data()->ElementAt(i).get(), size);
       dst += size;
     }
 
     map.Put(size, str);
   }
 }
 
+static already_AddRefed<LookupCacheV4> SetupLookupCache(
+    const nsACString& aName) {
+  nsCOMPtr<nsIFile> rootDir;
+  NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(rootDir));
+
+  nsAutoCString provider("test");
+  RefPtr<LookupCacheV4> lookup = new LookupCacheV4(aName, provider, rootDir);
+  lookup->Init();
+
+  return lookup.forget();
+}
+
 // Test setting prefix set with only 4-bytes prefixes
 TEST(UrlClassifierVLPrefixSet, FixedLengthSet) {
   srand(time(nullptr));
 
-  RefPtr<VariableLengthPrefixSet> pset = new VariableLengthPrefixSet;
-  pset->Init(NS_LITERAL_CSTRING("test"));
+  RefPtr<LookupCacheV4> cache = SetupLookupCache(NS_LITERAL_CSTRING("test"));
 
   PrefixStringMap map;
   _PrefixArray array = {
       _Prefix("alph"), _Prefix("brav"), _Prefix("char"),
       _Prefix("delt"), _Prefix("echo"), _Prefix("foxt"),
   };
 
   SetupPrefixMap(array, map);
-  pset->SetPrefixes(map);
-
-  DoExpectedLookup(pset, array);
+  cache->Build(map);
 
-  DoRandomLookup(pset, 1000, array);
-
-  CheckContent(pset, map);
+  DoExpectedLookup(cache, array);
+  DoRandomLookup(cache, 1000, array);
+  CheckContent(cache, map);
 
   // Run random test
   array.Clear();
   map.Clear();
 
   RandomPrefixes(1500, 4, 4, array);
 
   SetupPrefixMap(array, map);
-  pset->SetPrefixes(map);
-
-  DoExpectedLookup(pset, array);
+  cache->Build(map);
 
-  DoRandomLookup(pset, 1000, array);
-
-  CheckContent(pset, map);
+  DoExpectedLookup(cache, array);
+  DoRandomLookup(cache, 1000, array);
+  CheckContent(cache, map);
 }
 
 // Test setting prefix set with only 5~32 bytes prefixes
 TEST(UrlClassifierVLPrefixSet, VariableLengthSet) {
-  RefPtr<VariableLengthPrefixSet> pset = new VariableLengthPrefixSet;
-  pset->Init(NS_LITERAL_CSTRING("test"));
+  RefPtr<LookupCacheV4> cache = SetupLookupCache(NS_LITERAL_CSTRING("test"));
 
   PrefixStringMap map;
   _PrefixArray array = {
       _Prefix("bravo"),   _Prefix("charlie"),
       _Prefix("delta"),   _Prefix("EchoEchoEchoEchoEcho"),
       _Prefix("foxtrot"), _Prefix("GolfGolfGolfGolfGolfGolfGolfGolf"),
       _Prefix("hotel"),   _Prefix("november"),
       _Prefix("oscar"),   _Prefix("quebec"),
       _Prefix("romeo"),   _Prefix("sierrasierrasierrasierrasierra"),
       _Prefix("Tango"),   _Prefix("whiskey"),
       _Prefix("yankee"),  _Prefix("ZuluZuluZuluZulu")};
 
   SetupPrefixMap(array, map);
-  pset->SetPrefixes(map);
-
-  DoExpectedLookup(pset, array);
+  cache->Build(map);
 
-  DoRandomLookup(pset, 1000, array);
-
-  CheckContent(pset, map);
+  DoExpectedLookup(cache, array);
+  DoRandomLookup(cache, 1000, array);
+  CheckContent(cache, map);
 
   // Run random test
   array.Clear();
   map.Clear();
 
   RandomPrefixes(1500, 5, 32, array);
 
   SetupPrefixMap(array, map);
-  pset->SetPrefixes(map);
-
-  DoExpectedLookup(pset, array);
+  cache->Build(map);
 
-  DoRandomLookup(pset, 1000, array);
-
-  CheckContent(pset, map);
+  DoExpectedLookup(cache, array);
+  DoRandomLookup(cache, 1000, array);
+  CheckContent(cache, map);
 }
 
 // Test setting prefix set with both 4-bytes prefixes and 5~32 bytes prefixes
 TEST(UrlClassifierVLPrefixSet, MixedPrefixSet) {
-  RefPtr<VariableLengthPrefixSet> pset = new VariableLengthPrefixSet;
-  pset->Init(NS_LITERAL_CSTRING("test"));
+  RefPtr<LookupCacheV4> cache = SetupLookupCache(NS_LITERAL_CSTRING("test"));
 
   PrefixStringMap map;
   _PrefixArray array = {_Prefix("enus"),
                         _Prefix("apollo"),
                         _Prefix("mars"),
                         _Prefix("Hecatonchires cyclopes"),
                         _Prefix("vesta"),
                         _Prefix("neptunus"),
@@ -268,282 +271,323 @@ TEST(UrlClassifierVLPrefixSet, MixedPref
                         _Prefix("alcyoneus"),
                         _Prefix("hades"),
                         _Prefix("vulcanus"),
                         _Prefix("juno"),
                         _Prefix("mercury"),
                         _Prefix("Stheno, Euryale and Medusa")};
 
   SetupPrefixMap(array, map);
-  pset->SetPrefixes(map);
-
-  DoExpectedLookup(pset, array);
+  cache->Build(map);
 
-  DoRandomLookup(pset, 1000, array);
-
-  CheckContent(pset, map);
+  DoExpectedLookup(cache, array);
+  DoRandomLookup(cache, 1000, array);
+  CheckContent(cache, map);
 
   // Run random test
   array.Clear();
   map.Clear();
 
   RandomPrefixes(1500, 4, 32, array);
 
   SetupPrefixMap(array, map);
-  pset->SetPrefixes(map);
-
-  DoExpectedLookup(pset, array);
+  cache->Build(map);
 
-  DoRandomLookup(pset, 1000, array);
-
-  CheckContent(pset, map);
+  DoExpectedLookup(cache, array);
+  DoRandomLookup(cache, 1000, array);
+  CheckContent(cache, map);
 }
 
 // Test resetting prefix set
 TEST(UrlClassifierVLPrefixSet, ResetPrefix) {
-  RefPtr<VariableLengthPrefixSet> pset = new VariableLengthPrefixSet;
-  pset->Init(NS_LITERAL_CSTRING("test"));
+  RefPtr<LookupCacheV4> cache = SetupLookupCache(NS_LITERAL_CSTRING("test"));
 
   // First prefix set
   _PrefixArray array1 = {
       _Prefix("Iceland"),   _Prefix("Peru"),    _Prefix("Mexico"),
       _Prefix("Australia"), _Prefix("Japan"),   _Prefix("Egypt"),
       _Prefix("America"),   _Prefix("Finland"), _Prefix("Germany"),
       _Prefix("Italy"),     _Prefix("France"),  _Prefix("Taiwan"),
   };
   {
     PrefixStringMap map;
 
     SetupPrefixMap(array1, map);
-    pset->SetPrefixes(map);
+    cache->Build(map);
 
-    DoExpectedLookup(pset, array1);
+    DoExpectedLookup(cache, array1);
   }
 
   // Second
   _PrefixArray array2 = {
       _Prefix("Pikachu"),    _Prefix("Bulbasaur"), _Prefix("Charmander"),
       _Prefix("Blastoise"),  _Prefix("Pidgey"),    _Prefix("Mewtwo"),
       _Prefix("Jigglypuff"), _Prefix("Persian"),   _Prefix("Tentacool"),
       _Prefix("Onix"),       _Prefix("Eevee"),     _Prefix("Jynx"),
   };
   {
     PrefixStringMap map;
 
     SetupPrefixMap(array2, map);
-    pset->SetPrefixes(map);
+    cache->Build(map);
 
-    DoExpectedLookup(pset, array2);
+    DoExpectedLookup(cache, array2);
   }
 
   // Should not match any of the first prefix set
   uint32_t matchLength = 0;
   for (uint32_t i = 0; i < array1.Length(); i++) {
-    UniquePtr<nsACString> fullhash(CreateFullHash(array1[i]));
+    Completion complete;
+    complete.Assign(CreateFullHash(array1[i]));
 
-    pset->Matches(*fullhash, &matchLength);
+    // Find match for prefix-generated full hash
+    bool has, confirmed;
+    cache->Has(complete, &has, &matchLength, &confirmed);
+
     ASSERT_TRUE(matchLength == 0);
   }
 }
 
 // Test only set one 4-bytes prefix and one full-length prefix
 TEST(UrlClassifierVLPrefixSet, TinyPrefixSet) {
-  RefPtr<VariableLengthPrefixSet> pset = new VariableLengthPrefixSet;
-  pset->Init(NS_LITERAL_CSTRING("test"));
+  RefPtr<LookupCacheV4> cache = SetupLookupCache(NS_LITERAL_CSTRING("test"));
 
   PrefixStringMap map;
   _PrefixArray array = {
       _Prefix("AAAA"),
       _Prefix("11112222333344445555666677778888"),
   };
 
   SetupPrefixMap(array, map);
-  pset->SetPrefixes(map);
-
-  DoExpectedLookup(pset, array);
+  cache->Build(map);
 
-  DoRandomLookup(pset, 1000, array);
-
-  CheckContent(pset, map);
+  DoExpectedLookup(cache, array);
+  DoRandomLookup(cache, 1000, array);
+  CheckContent(cache, map);
 }
 
 // Test empty prefix set and IsEmpty function
 TEST(UrlClassifierVLPrefixSet, EmptyPrefixSet) {
-  RefPtr<VariableLengthPrefixSet> pset = new VariableLengthPrefixSet;
-  pset->Init(NS_LITERAL_CSTRING("test"));
+  RefPtr<LookupCacheV4> cache = SetupLookupCache(NS_LITERAL_CSTRING("test"));
 
-  bool empty;
-  pset->IsEmpty(&empty);
+  bool empty = cache->IsEmpty();
   ASSERT_TRUE(empty);
 
   PrefixStringMap map;
   _PrefixArray array1;
 
   // Lookup an empty array should never match
-  DoRandomLookup(pset, 100, array1);
+  DoRandomLookup(cache, 100, array1);
 
   // Insert an 4-bytes prefix, then IsEmpty should return false
   _PrefixArray array2 = {_Prefix("test")};
   SetupPrefixMap(array2, map);
-  pset->SetPrefixes(map);
+  cache->Build(map);
 
-  pset->IsEmpty(&empty);
+  empty = cache->IsEmpty();
   ASSERT_TRUE(!empty);
 
   _PrefixArray array3 = {_Prefix("test variable length")};
 
   // Insert an 5~32 bytes prefix, then IsEmpty should return false
   SetupPrefixMap(array3, map);
-  pset->SetPrefixes(map);
+  cache->Build(map);
 
-  pset->IsEmpty(&empty);
+  empty = cache->IsEmpty();
   ASSERT_TRUE(!empty);
 }
 
 // Test prefix size should only between 4~32 bytes
 TEST(UrlClassifierVLPrefixSet, MinMaxPrefixSet) {
-  RefPtr<VariableLengthPrefixSet> pset = new VariableLengthPrefixSet;
-  pset->Init(NS_LITERAL_CSTRING("test"));
+  RefPtr<LookupCacheV4> cache = SetupLookupCache(NS_LITERAL_CSTRING("test"));
 
   PrefixStringMap map;
   {
     _PrefixArray array = {_Prefix("1234"), _Prefix("ABCDEFGHIJKKMNOP"),
                           _Prefix("1aaa2bbb3ccc4ddd5eee6fff7ggg8hhh")};
 
     SetupPrefixMap(array, map);
-    nsresult rv = pset->SetPrefixes(map);
+    nsresult rv = cache->Build(map);
     ASSERT_TRUE(rv == NS_OK);
   }
 
   // Prefix size less than 4-bytes should fail
   {
     _PrefixArray array = {_Prefix("123")};
 
     SetupPrefixMap(array, map);
-    nsresult rv = pset->SetPrefixes(map);
+    nsresult rv = cache->Build(map);
     ASSERT_TRUE(NS_FAILED(rv));
   }
 
   // Prefix size greater than 32-bytes should fail
   {
     _PrefixArray array = {_Prefix("1aaa2bbb3ccc4ddd5eee6fff7ggg8hhh9")};
 
     SetupPrefixMap(array, map);
-    nsresult rv = pset->SetPrefixes(map);
+    nsresult rv = cache->Build(map);
     ASSERT_TRUE(NS_FAILED(rv));
   }
 }
 
 // Test save then load prefix set with only 4-bytes prefixes
 TEST(UrlClassifierVLPrefixSet, LoadSaveFixedLengthPrefixSet) {
-  RefPtr<VariableLengthPrefixSet> save = new VariableLengthPrefixSet;
-  save->Init(NS_LITERAL_CSTRING("test-save"));
-
+  nsCOMPtr<nsIFile> file;
   _PrefixArray array;
-  RandomPrefixes(10000, 4, 4, array);
-
   PrefixStringMap map;
-  SetupPrefixMap(array, map);
-  save->SetPrefixes(map);
 
-  DoExpectedLookup(save, array);
+  // Save
+  {
+    RefPtr<LookupCacheV4> save =
+        SetupLookupCache(NS_LITERAL_CSTRING("test-save"));
 
-  DoRandomLookup(save, 1000, array);
+    RandomPrefixes(10000, 4, 4, array);
 
-  CheckContent(save, map);
+    SetupPrefixMap(array, map);
+    save->Build(map);
 
-  nsCOMPtr<nsIFile> file;
-  NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(file));
-  file->Append(NS_LITERAL_STRING("test.vlpset"));
+    DoExpectedLookup(save, array);
+    DoRandomLookup(save, 1000, array);
+    CheckContent(save, map);
 
-  save->StoreToFile(file);
+    NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(file));
+    file->Append(NS_LITERAL_STRING("test.vlpset"));
+    save->StoreToFile(file);
+  }
 
-  RefPtr<VariableLengthPrefixSet> load = new VariableLengthPrefixSet;
-  load->Init(NS_LITERAL_CSTRING("test-load"));
-
-  load->LoadFromFile(file);
+  // Load
+  {
+    RefPtr<LookupCacheV4> load =
+        SetupLookupCache(NS_LITERAL_CSTRING("test-load"));
+    load->LoadFromFile(file);
 
-  DoExpectedLookup(load, array);
-
-  DoRandomLookup(load, 1000, array);
-
-  CheckContent(load, map);
+    DoExpectedLookup(load, array);
+    DoRandomLookup(load, 1000, array);
+    CheckContent(load, map);
+  }
 
   file->Remove(false);
 }
 
 // Test save then load prefix set with only 5~32 bytes prefixes
 TEST(UrlClassifierVLPrefixSet, LoadSaveVariableLengthPrefixSet) {
-  RefPtr<VariableLengthPrefixSet> save = new VariableLengthPrefixSet;
-  save->Init(NS_LITERAL_CSTRING("test-save"));
-
+  nsCOMPtr<nsIFile> file;
   _PrefixArray array;
-  RandomPrefixes(10000, 5, 32, array);
-
   PrefixStringMap map;
-  SetupPrefixMap(array, map);
-  save->SetPrefixes(map);
 
-  DoExpectedLookup(save, array);
+  // Save
+  {
+    RefPtr<LookupCacheV4> save =
+        SetupLookupCache(NS_LITERAL_CSTRING("test-save"));
 
-  DoRandomLookup(save, 1000, array);
+    RandomPrefixes(10000, 5, 32, array);
 
-  CheckContent(save, map);
+    SetupPrefixMap(array, map);
+    save->Build(map);
 
-  nsCOMPtr<nsIFile> file;
-  NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(file));
-  file->Append(NS_LITERAL_STRING("test.vlpset"));
+    DoExpectedLookup(save, array);
+    DoRandomLookup(save, 1000, array);
+    CheckContent(save, map);
 
-  save->StoreToFile(file);
+    NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(file));
+    file->Append(NS_LITERAL_STRING("test.vlpset"));
+    save->StoreToFile(file);
+  }
 
-  RefPtr<VariableLengthPrefixSet> load = new VariableLengthPrefixSet;
-  load->Init(NS_LITERAL_CSTRING("test-load"));
-
-  load->LoadFromFile(file);
+  // Load
+  {
+    RefPtr<LookupCacheV4> load =
+        SetupLookupCache(NS_LITERAL_CSTRING("test-load"));
+    load->LoadFromFile(file);
 
-  DoExpectedLookup(load, array);
-
-  DoRandomLookup(load, 1000, array);
-
-  CheckContent(load, map);
+    DoExpectedLookup(load, array);
+    DoRandomLookup(load, 1000, array);
+    CheckContent(load, map);
+  }
 
   file->Remove(false);
 }
 
 // Test save then load prefix with both 4 bytes prefixes and 5~32 bytes prefixes
 TEST(UrlClassifierVLPrefixSet, LoadSavePrefixSet) {
-  RefPtr<VariableLengthPrefixSet> save = new VariableLengthPrefixSet;
-  save->Init(NS_LITERAL_CSTRING("test-save"));
-
-  // Try to simulate the real case that most prefixes are 4bytes
+  nsCOMPtr<nsIFile> file;
   _PrefixArray array;
-  RandomPrefixes(20000, 4, 4, array);
-  RandomPrefixes(1000, 5, 32, array);
+  PrefixStringMap map;
 
-  PrefixStringMap map;
-  SetupPrefixMap(array, map);
-  save->SetPrefixes(map);
+  // Save
+  {
+    RefPtr<LookupCacheV4> save =
+        SetupLookupCache(NS_LITERAL_CSTRING("test-save"));
 
-  DoExpectedLookup(save, array);
+    // Try to simulate the real case that most prefixes are 4bytes
+    RandomPrefixes(20000, 4, 4, array);
+    RandomPrefixes(1000, 5, 32, array);
 
-  DoRandomLookup(save, 1000, array);
+    SetupPrefixMap(array, map);
+    save->Build(map);
 
-  CheckContent(save, map);
+    DoExpectedLookup(save, array);
+    DoRandomLookup(save, 1000, array);
+    CheckContent(save, map);
 
-  nsCOMPtr<nsIFile> file;
-  NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(file));
-  file->Append(NS_LITERAL_STRING("test.vlpset"));
-
-  save->StoreToFile(file);
+    NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(file));
+    file->Append(NS_LITERAL_STRING("test.vlpset"));
+    save->StoreToFile(file);
+  }
 
-  RefPtr<VariableLengthPrefixSet> load = new VariableLengthPrefixSet;
-  load->Init(NS_LITERAL_CSTRING("test-load"));
-
-  load->LoadFromFile(file);
+  // Load
+  {
+    RefPtr<LookupCacheV4> load =
+        SetupLookupCache(NS_LITERAL_CSTRING("test-load"));
+    load->LoadFromFile(file);
 
-  DoExpectedLookup(load, array);
-
-  DoRandomLookup(load, 1000, array);
-
-  CheckContent(load, map);
+    DoExpectedLookup(load, array);
+    DoRandomLookup(load, 1000, array);
+    CheckContent(load, map);
+  }
 
   file->Remove(false);
 }
+
+// This is for fixed-length prefixset
+TEST(UrlClassifierVLPrefixSet, LoadSaveNoDelta) {
+  nsCOMPtr<nsIFile> file;
+  _PrefixArray array;
+  PrefixStringMap map;
+
+  for (uint32_t i = 0; i < 100; i++) {
+    // construct a tree without deltas by making the distance
+    // between entries larger than 16 bits
+    uint32_t v = ((1 << 16) + 1) * i;
+    nsCString* ele = array.AppendElement();
+    ele->AppendASCII(reinterpret_cast<const char*>(&v), 4);
+  }
+
+  // Save
+  {
+    RefPtr<LookupCacheV4> save =
+        SetupLookupCache(NS_LITERAL_CSTRING("test-save"));
+
+    SetupPrefixMap(array, map);
+    save->Build(map);
+
+    DoExpectedLookup(save, array);
+    DoRandomLookup(save, 1000, array);
+    CheckContent(save, map);
+
+    NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(file));
+    file->Append(NS_LITERAL_STRING("test.vlpset"));
+    save->StoreToFile(file);
+  }
+
+  // Load
+  {
+    RefPtr<LookupCacheV4> load =
+        SetupLookupCache(NS_LITERAL_CSTRING("test-load"));
+    load->LoadFromFile(file);
+
+    DoExpectedLookup(load, array);
+    DoRandomLookup(load, 1000, array);
+    CheckContent(load, map);
+  }
+
+  file->Remove(false);
+}
--- a/toolkit/components/url-classifier/tests/unit/test_prefixset.js
+++ b/toolkit/components/url-classifier/tests/unit/test_prefixset.js
@@ -135,97 +135,37 @@ function testReSetPrefixes() {
   for (let i = 0; i < prefixes.length; i++) {
     Assert.ok(!wrappedProbe(pset, prefixes[i]));
   }
 
 
   checkContents(pset, secondPrefixes);
 }
 
-function testLoadSaveLargeSet() {
-  let N = 1000;
-  let arr = [];
-
-  for (let i = 0; i < N; i++) {
-    let randInt = Math.floor(Math.random() * Math.pow(2, 32));
-    arr.push(randInt);
-  }
-
-  arr.sort((x, y) => x - y);
-
-  let pset = newPset();
-  pset.setPrefixes(arr, arr.length);
-
-  doExpectedLookups(pset, arr, 1);
-  doRandomLookups(pset, arr, 1000);
-
-  checkContents(pset, arr);
-
-  // Now try to save, restore, and redo the lookups
-  var file = Services.dirsvc.get("ProfLD", Ci.nsIFile);
-  file.append("testLarge.pset");
-
-  pset.storeToFile(file);
-
-  let psetLoaded = newPset();
-  psetLoaded.loadFromFile(file);
-
-  doExpectedLookups(psetLoaded, arr, 1);
-  doRandomLookups(psetLoaded, arr, 1000);
-
-  checkContents(psetLoaded, arr);
-}
-
 function testTinySet() {
   let pset = Cc["@mozilla.org/url-classifier/prefixset;1"]
                .createInstance(Ci.nsIUrlClassifierPrefixSet);
   let prefixes = [1];
   pset.setPrefixes(prefixes, prefixes.length);
 
   Assert.ok(wrappedProbe(pset, 1));
   Assert.ok(!wrappedProbe(pset, 100000));
   checkContents(pset, prefixes);
 
   prefixes = [];
   pset.setPrefixes(prefixes, prefixes.length);
   Assert.ok(!wrappedProbe(pset, 1));
   checkContents(pset, prefixes);
 }
 
-function testLoadSaveNoDelta() {
-  let N = 100;
-  let arr = [];
-
-  for (let i = 0; i < N; i++) {
-    // construct a tree without deltas by making the distance
-    // between entries larger than 16 bits
-    arr.push(((1 << 16) + 1) * i);
-  }
-
-  let pset = newPset();
-  pset.setPrefixes(arr, arr.length);
-
-  doExpectedLookups(pset, arr, 1);
-
-  var file = Services.dirsvc.get("ProfLD", Ci.nsIFile);
-  file.append("testNoDelta.pset");
-
-  pset.storeToFile(file);
-  pset.loadFromFile(file);
-
-  doExpectedLookups(pset, arr, 1);
-}
-
 var tests = [testBasicPset,
              testSimplePset,
              testReSetPrefixes,
-             testLoadSaveLargeSet,
              testDuplicates,
-             testTinySet,
-             testLoadSaveNoDelta];
+             testTinySet];
 
 function run_test() {
   // None of the tests use |executeSoon| or any sort of callbacks, so we can
   // just run them in succession.
   for (let i = 0; i < tests.length; i++) {
     dump("Running " + tests[i].name + "\n");
     tests[i]();
   }