Backed out changesets af0dda676cb7, 550e2f5b4224, and bb76828c5f22 (bug 842828) for B2G bustage and xpcshell failures.
authorRyan VanderMeulen <ryanvm@gmail.com>
Wed, 25 Sep 2013 11:50:51 -0400
changeset 148683 2ac30931b19b9caae6706947c6c515885cbd6302
parent 148682 ff7c4578831c63fa56838113b7d0c349ed63b269
child 148684 0d367aa1e6c94c26d47e63dbd988402ae6d4cc9c
push id25352
push userkwierso@gmail.com
push dateThu, 26 Sep 2013 03:27:24 +0000
treeherdermozilla-central@94548c13fd47 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs842828
milestone27.0a1
backs outaf0dda676cb795cbc6509d74ae1faf604fcebcbc
550e2f5b42246ea41f8cf28eff4b71541cfee8cf
bb76828c5f22e236f848fa7df7aacd0b58522315
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
Backed out changesets af0dda676cb7, 550e2f5b4224, and bb76828c5f22 (bug 842828) for B2G bustage and xpcshell failures. CLOSED TREE
b2g/app/b2g.js
browser/app/profile/firefox.js
browser/metro/profile/metro.js
mobile/android/app/mobile.js
modules/libpref/src/init/all.js
toolkit/components/build/nsToolkitCompsCID.h
toolkit/components/build/nsToolkitCompsModule.cpp
toolkit/components/downloads/ApplicationReputation.cpp
toolkit/components/downloads/ApplicationReputation.h
toolkit/components/downloads/nsIApplicationReputation.idl
toolkit/components/downloads/test/unit/data/digest.chunk
toolkit/components/downloads/test/unit/test_app_rep.js
toolkit/components/url-classifier/SafeBrowsing.jsm
toolkit/components/url-classifier/moz.build
toolkit/components/url-classifier/nsUrlClassifierDBService.cpp
toolkit/components/url-classifier/nsUrlClassifierDBService.h
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -325,16 +325,19 @@ pref("browser.geolocation.warning.infoUR
 
 // Name of the about: page contributed by safebrowsing to handle display of error
 // pages on phishing/malware hits.  (bug 399233)
 pref("urlclassifier.alternate_error_page", "blocked");
 
 // The number of random entries to send with a gethash request.
 pref("urlclassifier.gethashnoise", 4);
 
+// The list of tables that use the gethash request to confirm partial results.
+pref("urlclassifier.gethashtables", "goog-phish-shavar,goog-malware-shavar");
+
 // If an urlclassifier table has not been updated in this number of seconds,
 // a gethash request will be forced to check that the result is still in
 // the database.
 pref("urlclassifier.max-complete-age", 2700);
 
 // URL for checking the reason for a malware warning.
 pref("browser.safebrowsing.malware.reportURL", "http://safebrowsing.clients.google.com/safebrowsing/diagnostic?client=%NAME%&hl=%LOCALE%&site=");
 #endif
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -798,16 +798,19 @@ pref("browser.safebrowsing.id", "navclie
 
 // Name of the about: page contributed by safebrowsing to handle display of error
 // pages on phishing/malware hits.  (bug 399233)
 pref("urlclassifier.alternate_error_page", "blocked");
 
 // The number of random entries to send with a gethash request.
 pref("urlclassifier.gethashnoise", 4);
 
+// The list of tables that use the gethash request to confirm partial results.
+pref("urlclassifier.gethashtables", "goog-phish-shavar,goog-malware-shavar");
+
 // If an urlclassifier table has not been updated in this number of seconds,
 // a gethash request will be forced to check that the result is still in
 // the database.
 pref("urlclassifier.max-complete-age", 2700);
 #endif
 
 pref("browser.geolocation.warning.infoURL", "https://www.mozilla.org/%LOCALE%/firefox/geolocation/");
 
--- a/browser/metro/profile/metro.js
+++ b/browser/metro/profile/metro.js
@@ -602,16 +602,19 @@ pref("browser.geolocation.warning.infoUR
 
 // Name of the about: page contributed by safebrowsing to handle display of error
 // pages on phishing/malware hits.  (bug 399233)
 pref("urlclassifier.alternate_error_page", "blocked");
 
 // The number of random entries to send with a gethash request.
 pref("urlclassifier.gethashnoise", 4);
 
+// The list of tables that use the gethash request to confirm partial results.
+pref("urlclassifier.gethashtables", "goog-phish-shavar,goog-malware-shavar");
+
 // If an urlclassifier table has not been updated in this number of seconds,
 // a gethash request will be forced to check that the result is still in
 // the database.
 pref("urlclassifier.max-complete-age", 2700);
 
 // Maximum size of the sqlite3 cache during an update, in bytes
 pref("urlclassifier.updatecachemax", 41943040);
 
--- a/mobile/android/app/mobile.js
+++ b/mobile/android/app/mobile.js
@@ -595,16 +595,19 @@ pref("browser.safebrowsing.id", @MOZ_APP
 
 // Name of the about: page contributed by safebrowsing to handle display of error
 // pages on phishing/malware hits.  (bug 399233)
 pref("urlclassifier.alternate_error_page", "blocked");
 
 // The number of random entries to send with a gethash request.
 pref("urlclassifier.gethashnoise", 4);
 
+// The list of tables that use the gethash request to confirm partial results.
+pref("urlclassifier.gethashtables", "goog-phish-shavar,goog-malware-shavar");
+
 // If an urlclassifier table has not been updated in this number of seconds,
 // a gethash request will be forced to check that the result is still in
 // the database.
 pref("urlclassifier.max-complete-age", 2700);
 #endif
 
 // True if this is the first time we are showing about:firstrun
 pref("browser.firstrun.show.uidiscovery", true);
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -4423,14 +4423,8 @@ pref("dom.forms.inputmode", false);
 pref("dom.forms.inputmode", true);
 #endif
 
 // Telephony API
 pref("dom.telephony.enabled", false);
 
 // DOM Inter-App Communication API.
 pref("dom.inter-app-communication-api.enabled", false);
-
-// The tables used for Safebrowsing phishing and malware checks.
-pref("urlclassifier.malware_table", "goog-malware-shavar");
-pref("urlclassifier.phish_table", "goog-phish-shavar");
-pref("urlclassifier.download_block_table", "goog-badbinurl-shavar");
-pref("urlclassifier.download_allow_table", "goog-downloadwhite-digest256");
--- a/toolkit/components/build/nsToolkitCompsCID.h
+++ b/toolkit/components/build/nsToolkitCompsCID.h
@@ -170,13 +170,19 @@
 #define NS_FAVICONSERVICE_CID \
 { 0x984e3259, 0x9266, 0x49cf, { 0xb6, 0x05, 0x60, 0xb0, 0x22, 0xa0, 0x07, 0x56 } }
 
 #if defined(USE_MOZ_UPDATER)
 #define NS_UPDATEPROCESSOR_CID \
 { 0xf3dcf644, 0x79e8, 0x4f59, { 0xa1, 0xbb, 0x87, 0x84, 0x54, 0x48, 0x8e, 0xf9 } }
 #endif
 
+#define NS_APPLICATION_REPUTATION_QUERY_CONTRACTID \
+  "@mozilla.org/downloads/application-reputation-query;1"
+
+#define NS_APPLICATION_REPUTATION_QUERY_CID \
+{ 0x857da2c0, 0xcfe5, 0x11e2, { 0x8b, 0x8b, 0x08, 0x00, 0x20, 0x0c, 0x9a, 0x66 } }
+
 #define NS_APPLICATION_REPUTATION_SERVICE_CONTRACTID \
   "@mozilla.org/downloads/application-reputation-service;1"
 
 #define NS_APPLICATION_REPUTATION_SERVICE_CID \
 { 0x8576c950, 0xf4a2, 0x11e2, { 0xb7, 0x78, 0x08, 0x00, 0x20, 0x0c, 0x9a, 0x66 } }
--- a/toolkit/components/build/nsToolkitCompsModule.cpp
+++ b/toolkit/components/build/nsToolkitCompsModule.cpp
@@ -74,23 +74,25 @@ nsUrlClassifierDBServiceConstructor(nsIS
     /* NS_ADDREF(inst); */
     rv = inst->QueryInterface(aIID, aResult);
     NS_RELEASE(inst);
 
     return rv;
 }
 #endif
 
+NS_GENERIC_FACTORY_CONSTRUCTOR(ApplicationReputationQuery)
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(ApplicationReputationService,
                                          ApplicationReputationService::GetSingleton)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsBrowserStatusFilter)
 #if defined(USE_MOZ_UPDATER)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsUpdateProcessor)
 #endif
 
+NS_DEFINE_NAMED_CID(NS_APPLICATION_REPUTATION_QUERY_CID);
 NS_DEFINE_NAMED_CID(NS_APPLICATION_REPUTATION_SERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_TOOLKIT_APPSTARTUP_CID);
 NS_DEFINE_NAMED_CID(NS_USERINFO_CID);
 NS_DEFINE_NAMED_CID(NS_ALERTSSERVICE_CID);
 #if defined(XP_WIN) && !defined(MOZ_DISABLE_PARENTAL_CONTROLS)
 NS_DEFINE_NAMED_CID(NS_PARENTALCONTROLSSERVICE_CID);
 #endif
 NS_DEFINE_NAMED_CID(NS_DOWNLOADMANAGER_CID);
@@ -106,16 +108,17 @@ NS_DEFINE_NAMED_CID(NS_URLCLASSIFIERUTIL
 #endif
 NS_DEFINE_NAMED_CID(NS_BROWSERSTATUSFILTER_CID);
 NS_DEFINE_NAMED_CID(NS_CHARSETMENU_CID);
 #if defined(USE_MOZ_UPDATER)
 NS_DEFINE_NAMED_CID(NS_UPDATEPROCESSOR_CID);
 #endif
 
 static const mozilla::Module::CIDEntry kToolkitCIDs[] = {
+  { &kNS_APPLICATION_REPUTATION_QUERY_CID, false, NULL, ApplicationReputationQueryConstructor },
   { &kNS_APPLICATION_REPUTATION_SERVICE_CID, false, NULL, ApplicationReputationServiceConstructor },
   { &kNS_TOOLKIT_APPSTARTUP_CID, false, NULL, nsAppStartupConstructor },
   { &kNS_USERINFO_CID, false, NULL, nsUserInfoConstructor },
   { &kNS_ALERTSSERVICE_CID, false, NULL, nsAlertsServiceConstructor },
 #if defined(XP_WIN) && !defined(MOZ_DISABLE_PARENTAL_CONTROLS)
   { &kNS_PARENTALCONTROLSSERVICE_CID, false, NULL, nsParentalControlsServiceWinConstructor },
 #endif
   { &kNS_DOWNLOADMANAGER_CID, false, NULL, nsDownloadManagerConstructor },
@@ -133,16 +136,17 @@ static const mozilla::Module::CIDEntry k
   { &kNS_CHARSETMENU_CID, false, NULL, NS_NewCharsetMenu },
 #if defined(USE_MOZ_UPDATER)
   { &kNS_UPDATEPROCESSOR_CID, false, NULL, nsUpdateProcessorConstructor },
 #endif
   { NULL }
 };
 
 static const mozilla::Module::ContractIDEntry kToolkitContracts[] = {
+  { NS_APPLICATION_REPUTATION_QUERY_CONTRACTID, &kNS_APPLICATION_REPUTATION_QUERY_CID },
   { NS_APPLICATION_REPUTATION_SERVICE_CONTRACTID, &kNS_APPLICATION_REPUTATION_SERVICE_CID },
   { NS_APPSTARTUP_CONTRACTID, &kNS_TOOLKIT_APPSTARTUP_CID },
   { NS_USERINFO_CONTRACTID, &kNS_USERINFO_CID },
   { NS_ALERTSERVICE_CONTRACTID, &kNS_ALERTSSERVICE_CID },
 #if defined(XP_WIN) && !defined(MOZ_DISABLE_PARENTAL_CONTROLS)
   { NS_PARENTALCONTROLSSERVICE_CONTRACTID, &kNS_PARENTALCONTROLSSERVICE_CID },
 #endif
   { NS_DOWNLOADMANAGER_CONTRACTID, &kNS_DOWNLOADMANAGER_CID },
--- a/toolkit/components/downloads/ApplicationReputation.cpp
+++ b/toolkit/components/downloads/ApplicationReputation.cpp
@@ -6,170 +6,131 @@
 
 #include "ApplicationReputation.h"
 #include "csd.pb.h"
 
 #include "nsIApplicationReputation.h"
 #include "nsIChannel.h"
 #include "nsIHttpChannel.h"
 #include "nsIIOService.h"
+#include "nsIObserverService.h"
 #include "nsIPrefService.h"
-#include "nsIScriptSecurityManager.h"
 #include "nsIStreamListener.h"
 #include "nsIStringStream.h"
 #include "nsIUploadChannel2.h"
 #include "nsIURI.h"
 
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
 
 #include "nsCOMPtr.h"
 #include "nsDebug.h"
 #include "nsError.h"
 #include "nsNetCID.h"
-#include "nsReadableUtils.h"
 #include "nsServiceManagerUtils.h"
 #include "nsString.h"
 #include "nsThreadUtils.h"
 #include "nsXPCOMStrings.h"
 
 using mozilla::Preferences;
 
 // Preferences that we need to initialize the query. We may need another
 // preference than browser.safebrowsing.malware.enabled, or simply use
 // browser.safebrowsing.appRepURL. See bug 887041.
 #define PREF_SB_APP_REP_URL "browser.safebrowsing.appRepURL"
 #define PREF_SB_MALWARE_ENABLED "browser.safebrowsing.malware.enabled"
 #define PREF_GENERAL_LOCALE "general.useragent.locale"
-#define PREF_DOWNLOAD_BLOCK_TABLE "urlclassifier.download_block_table"
-#define PREF_DOWNLOAD_ALLOW_TABLE "urlclassifier.download_allow_table"
 
-/**
- * Keep track of pending lookups. Once the ApplicationReputationService creates
- * this, it is guaranteed to call mCallback. This class is private to
- * ApplicationReputationService.
- */
-class PendingLookup MOZ_FINAL :
-  public nsIStreamListener,
-  public nsIUrlClassifierCallback {
-public:
-  NS_DECL_ISUPPORTS
-  NS_DECL_NSIREQUESTOBSERVER
-  NS_DECL_NSISTREAMLISTENER
-  NS_DECL_NSIURLCLASSIFIERCALLBACK
-  PendingLookup(nsIApplicationReputationQuery* aQuery,
-                nsIApplicationReputationCallback* aCallback);
-  ~PendingLookup();
+NS_IMPL_ISUPPORTS1(ApplicationReputationService, nsIApplicationReputationService)
+
+ApplicationReputationService* ApplicationReputationService::gApplicationReputationService = nullptr;
 
-private:
-  nsCOMPtr<nsIApplicationReputationQuery> mQuery;
-  nsCOMPtr<nsIApplicationReputationCallback> mCallback;
-  /**
-   * The response from the application reputation query. This is read in chunks
-   * as part of our nsIStreamListener implementation and may contain embedded
-   * NULLs.
-   */
-  nsCString mResponse;
-  /**
-   * Clean up and call the callback. PendingLookup must not be used after this
-   * function is called.
-   */
-  nsresult OnComplete(bool shouldBlock, nsresult rv);
-  /**
-   * Wrapper function for nsIStreamListener.onStopRequest to make it easy to
-   * guarantee calling the callback
-   */
-  nsresult OnStopRequestInternal(nsIRequest *aRequest,
-                                 nsISupports *aContext,
-                                 nsresult aResult,
-                                 bool* aShouldBlock);
-  /**
-   * Sends a query to the remote application reputation service. Returns NS_OK
-   * on success.
-   */
-  nsresult SendRemoteQuery();
-};
+ApplicationReputationService *
+ApplicationReputationService::GetSingleton()
+{
+  if (gApplicationReputationService) {
+    NS_ADDREF(gApplicationReputationService);
+    return gApplicationReputationService;
+  }
 
-NS_IMPL_ISUPPORTS3(PendingLookup,
-                   nsIStreamListener,
-                   nsIRequestObserver,
-                   nsIUrlClassifierCallback)
+  gApplicationReputationService = new ApplicationReputationService();
+  if (gApplicationReputationService) {
+    NS_ADDREF(gApplicationReputationService);
+  }
 
-PendingLookup::PendingLookup(nsIApplicationReputationQuery* aQuery,
-                             nsIApplicationReputationCallback* aCallback) :
-  mQuery(aQuery),
-  mCallback(aCallback) {
+  return gApplicationReputationService;
 }
 
-PendingLookup::~PendingLookup() {
-}
+ApplicationReputationService::ApplicationReputationService() { }
+ApplicationReputationService::~ApplicationReputationService() { }
 
-nsresult
-PendingLookup::OnComplete(bool shouldBlock, nsresult rv) {
-  nsresult res = mCallback->OnComplete(shouldBlock, rv);
-  return res;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-//// nsIUrlClassifierCallback
 NS_IMETHODIMP
-PendingLookup::HandleEvent(const nsACString& tables) {
-  // HandleEvent is guaranteed to call the callback if either the URL can be
-  // classified locally, or if there is an error sending the remote lookup.
-  // Allow listing trumps block listing.
-  nsCString allow_list;
-  Preferences::GetCString(PREF_DOWNLOAD_ALLOW_TABLE, &allow_list);
-  if (FindInReadable(tables, allow_list)) {
-    return OnComplete(false, NS_OK);
-  }
+ApplicationReputationService::QueryReputation(
+  nsIApplicationReputationQuery* aQuery,
+  nsIApplicationReputationCallback* aCallback) {
+  NS_ENSURE_ARG_POINTER(aQuery);
+  NS_ENSURE_ARG_POINTER(aCallback);
 
-  nsCString block_list;
-  Preferences::GetCString(PREF_DOWNLOAD_BLOCK_TABLE, &block_list);
-  if (FindInReadable(tables, block_list)) {
-    return OnComplete(true, NS_OK);
-  }
-
-  nsresult rv = SendRemoteQuery();
+  nsresult rv = QueryReputationInternal(aQuery, aCallback);
   if (NS_FAILED(rv)) {
-    return OnComplete(false, rv);
+    aCallback->OnComplete(false, rv);
+    aCallback = nullptr;
   }
   return NS_OK;
 }
 
 nsresult
-PendingLookup::SendRemoteQuery() {
-  // We did not find a local result, so fire off the query to the application
-  // reputation service.
+ApplicationReputationService::QueryReputationInternal(
+  nsIApplicationReputationQuery* aQuery,
+  nsIApplicationReputationCallback* aCallback) {
+  nsresult rv;
+  aQuery->SetCallback(aCallback);
+
+  // If malware checks aren't enabled, don't query application reputation.
+  if (!Preferences::GetBool(PREF_SB_MALWARE_ENABLED, false)) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  // If there is no service URL for querying application reputation, abort.
+  nsCString serviceUrl;
+  NS_ENSURE_SUCCESS(Preferences::GetCString(PREF_SB_APP_REP_URL, &serviceUrl),
+                    NS_ERROR_NOT_AVAILABLE);
+  if (serviceUrl.EqualsLiteral("")) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
   safe_browsing::ClientDownloadRequest req;
-  nsCOMPtr<nsIURI> uri;
-  nsresult rv;
-  rv = mQuery->GetSourceURI(getter_AddRefs(uri));
+
+  nsCString spec;
+  nsCOMPtr<nsIURI> aURI;
+  rv = aQuery->GetSourceURI(getter_AddRefs(aURI));
   NS_ENSURE_SUCCESS(rv, rv);
-  nsCString spec;
-  rv = uri->GetSpec(spec);
+  // If the URI hasn't been set, bail
+  NS_ENSURE_STATE(aURI);
+  rv = aURI->GetSpec(spec);
   NS_ENSURE_SUCCESS(rv, rv);
+
   req.set_url(spec.get());
-
   uint32_t fileSize;
-  rv = mQuery->GetFileSize(&fileSize);
+  rv = aQuery->GetFileSize(&fileSize);
   NS_ENSURE_SUCCESS(rv, rv);
   req.set_length(fileSize);
   // We have no way of knowing whether or not a user initiated the download.
   req.set_user_initiated(false);
 
   nsCString locale;
   NS_ENSURE_SUCCESS(Preferences::GetCString(PREF_GENERAL_LOCALE, &locale),
                     NS_ERROR_NOT_AVAILABLE);
   req.set_locale(locale.get());
   nsCString sha256Hash;
-  rv = mQuery->GetSha256Hash(sha256Hash);
+  rv = aQuery->GetSha256Hash(sha256Hash);
   NS_ENSURE_SUCCESS(rv, rv);
   req.mutable_digests()->set_sha256(sha256Hash.Data());
   nsString fileName;
-  rv = mQuery->GetSuggestedFileName(fileName);
+  rv = aQuery->GetSuggestedFileName(fileName);
   NS_ENSURE_SUCCESS(rv, rv);
   req.set_file_basename(NS_ConvertUTF16toUTF8(fileName).get());
 
   // Serialize the protocol buffer to a string. This can only fail if we are
   // out of memory, or if the protocol buffer req is missing required fields
   // (only the URL for now).
   std::string serialized;
   if (!req.SerializeToString(&serialized)) {
@@ -184,19 +145,16 @@ PendingLookup::SendRemoteQuery() {
   rv = sstream->SetData(serialized.c_str(), serialized.length());
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIIOService> ios = do_GetService(NS_IOSERVICE_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Set up the channel to transmit the request to the service.
   nsCOMPtr<nsIChannel> channel;
-  nsCString serviceUrl;
-  NS_ENSURE_SUCCESS(Preferences::GetCString(PREF_SB_APP_REP_URL, &serviceUrl),
-                    NS_ERROR_NOT_AVAILABLE);
   rv = ios->NewChannel(serviceUrl, nullptr, nullptr, getter_AddRefs(channel));
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel, &rv));
   NS_ENSURE_SUCCESS(rv, rv);
 
   // See bug 887044 for finalizing the user agent.
   const nsCString userAgent = NS_LITERAL_CSTRING("CsdTesting/Mozilla");
@@ -208,71 +166,154 @@ PendingLookup::SendRemoteQuery() {
   nsCOMPtr<nsIUploadChannel2> uploadChannel = do_QueryInterface(channel, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = uploadChannel->ExplicitSetUploadStream(sstream,
     NS_LITERAL_CSTRING("application/octet-stream"), serialized.size(),
     NS_LITERAL_CSTRING("POST"), false);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  rv = channel->AsyncOpen(this, nullptr);
+  nsCOMPtr<nsIStreamListener> listener = do_QueryInterface(aQuery, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+  rv = channel->AsyncOpen(listener, nullptr);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
+NS_IMPL_ISUPPORTS3(ApplicationReputationQuery,
+                   nsIApplicationReputationQuery,
+                   nsIStreamListener,
+                   nsIRequestObserver)
+
+ApplicationReputationQuery::ApplicationReputationQuery() :
+  mURI(nullptr),
+  mFileSize(0),
+  mCallback(nullptr) {
+}
+
+ApplicationReputationQuery::~ApplicationReputationQuery() {
+}
+
+NS_IMETHODIMP
+ApplicationReputationQuery::GetSourceURI(nsIURI** aURI) {
+  *aURI = mURI;
+  NS_IF_ADDREF(*aURI);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+ApplicationReputationQuery::SetSourceURI(nsIURI* aURI) {
+  mURI = aURI;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+ApplicationReputationQuery::GetSuggestedFileName(
+  nsAString& aSuggestedFileName) {
+  aSuggestedFileName = mSuggestedFileName;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+ApplicationReputationQuery::SetSuggestedFileName(
+  const nsAString& aSuggestedFileName) {
+  mSuggestedFileName = aSuggestedFileName;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+ApplicationReputationQuery::GetFileSize(uint32_t* aFileSize) {
+  *aFileSize = mFileSize;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+ApplicationReputationQuery::SetFileSize(uint32_t aFileSize) {
+  mFileSize = aFileSize;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+ApplicationReputationQuery::GetSha256Hash(nsACString& aSha256Hash) {
+  aSha256Hash = mSha256Hash;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+ApplicationReputationQuery::SetSha256Hash(const nsACString& aSha256Hash) {
+  mSha256Hash = aSha256Hash;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+ApplicationReputationQuery::GetCallback(
+  nsIApplicationReputationCallback** aCallback) {
+  *aCallback = mCallback;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+ApplicationReputationQuery::SetCallback(
+  nsIApplicationReputationCallback* aCallback) {
+  mCallback = aCallback;
+  return NS_OK;
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 //// nsIStreamListener
+
 static NS_METHOD
 AppendSegmentToString(nsIInputStream* inputStream,
                       void *closure,
                       const char *rawSegment,
                       uint32_t toOffset,
                       uint32_t count,
                       uint32_t *writeCount) {
   nsAutoCString* decodedData = static_cast<nsAutoCString*>(closure);
   decodedData->Append(rawSegment, count);
   *writeCount = count;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-PendingLookup::OnDataAvailable(nsIRequest *aRequest,
-                               nsISupports *aContext,
-                               nsIInputStream *aStream,
-                               uint64_t offset,
-                               uint32_t count) {
+ApplicationReputationQuery::OnDataAvailable(nsIRequest *aRequest,
+                                            nsISupports *aContext,
+                                            nsIInputStream *aStream,
+                                            uint64_t offset,
+                                            uint32_t count) {
   uint32_t read;
   return aStream->ReadSegments(AppendSegmentToString, &mResponse, count, &read);
 }
 
 NS_IMETHODIMP
-PendingLookup::OnStartRequest(nsIRequest *aRequest,
-                              nsISupports *aContext) {
+ApplicationReputationQuery::OnStartRequest(nsIRequest *aRequest,
+                                      nsISupports *aContext) {
   return NS_OK;
 }
 
 NS_IMETHODIMP
-PendingLookup::OnStopRequest(nsIRequest *aRequest,
-                             nsISupports *aContext,
-                             nsresult aResult) {
+ApplicationReputationQuery::OnStopRequest(nsIRequest *aRequest,
+                                          nsISupports *aContext,
+                                          nsresult aResult) {
   NS_ENSURE_STATE(mCallback);
 
   bool shouldBlock = false;
   nsresult rv = OnStopRequestInternal(aRequest, aContext, aResult,
                                       &shouldBlock);
-  OnComplete(shouldBlock, rv);
+  mCallback->OnComplete(shouldBlock, rv);
+  mCallback = nullptr;
   return rv;
 }
 
 nsresult
-PendingLookup::OnStopRequestInternal(nsIRequest *aRequest,
-                                     nsISupports *aContext,
-                                     nsresult aResult,
-                                     bool* aShouldBlock) {
+ApplicationReputationQuery::OnStopRequestInternal(nsIRequest *aRequest,
+                                                  nsISupports *aContext,
+                                                  nsresult aResult,
+                                                  bool* aShouldBlock) {
   *aShouldBlock = false;
   nsresult rv;
   nsCOMPtr<nsIHttpChannel> channel = do_QueryInterface(aRequest, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   uint32_t status = 0;
   rv = channel->GetResponseStatus(&status);
   NS_ENSURE_SUCCESS(rv, rv);
@@ -291,107 +332,8 @@ PendingLookup::OnStopRequestInternal(nsI
   // There are several more verdicts, but we only respect one for now and treat
   // everything else as SAFE.
   if (response.verdict() == safe_browsing::ClientDownloadResponse::DANGEROUS) {
     *aShouldBlock = true;
   }
 
   return NS_OK;
 }
-
-NS_IMPL_ISUPPORTS1(ApplicationReputationService,
-                   nsIApplicationReputationService)
-
-ApplicationReputationService*
-  ApplicationReputationService::gApplicationReputationService = nullptr;
-
-ApplicationReputationService*
-ApplicationReputationService::GetSingleton()
-{
-  if (gApplicationReputationService) {
-    NS_ADDREF(gApplicationReputationService);
-    return gApplicationReputationService;
-  }
-
-  // We're not initialized yet.
-  gApplicationReputationService = new ApplicationReputationService();
-  if (gApplicationReputationService) {
-    NS_ADDREF(gApplicationReputationService);
-  }
-
-  return gApplicationReputationService;
-}
-
-ApplicationReputationService::ApplicationReputationService() :
-  mDBService(nullptr),
-  mSecurityManager(nullptr) {
-}
-
-ApplicationReputationService::~ApplicationReputationService() {
-}
-
-NS_IMETHODIMP
-ApplicationReputationService::QueryReputation(
-    nsIApplicationReputationQuery* aQuery,
-    nsIApplicationReputationCallback* aCallback) {
-  NS_ENSURE_ARG_POINTER(aQuery);
-  NS_ENSURE_ARG_POINTER(aCallback);
-
-  nsresult rv = QueryReputationInternal(aQuery, aCallback);
-  if (NS_FAILED(rv)) {
-    aCallback->OnComplete(false, rv);
-  }
-  return NS_OK;
-}
-
-nsresult ApplicationReputationService::QueryReputationInternal(
-  nsIApplicationReputationQuery* aQuery,
-  nsIApplicationReputationCallback* aCallback) {
-  // Lazily instantiate mDBService and mSecurityManager
-  nsresult rv;
-  if (!mDBService) {
-    mDBService = do_GetService(NS_URLCLASSIFIERDBSERVICE_CONTRACTID, &rv);
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-  if (!mSecurityManager) {
-    mSecurityManager = do_GetService("@mozilla.org/scriptsecuritymanager;1",
-                                     &rv);
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-  // If malware checks aren't enabled, don't query application reputation.
-  if (!Preferences::GetBool(PREF_SB_MALWARE_ENABLED, false)) {
-    return NS_ERROR_NOT_AVAILABLE;
-  }
-
-  // If there is no service URL for querying application reputation, abort.
-  nsCString serviceUrl;
-  NS_ENSURE_SUCCESS(Preferences::GetCString(PREF_SB_APP_REP_URL, &serviceUrl),
-                    NS_ERROR_NOT_AVAILABLE);
-  if (serviceUrl.EqualsLiteral("")) {
-    return NS_ERROR_NOT_AVAILABLE;
-  }
-
-  // We must be able to create a principal and query local lists.
-  NS_ENSURE_STATE(mDBService);
-  NS_ENSURE_STATE(mSecurityManager);
-
-  // Create a new pending lookup.
-  nsRefPtr<PendingLookup> lookup(new PendingLookup(aQuery, aCallback));
-  NS_ENSURE_STATE(lookup);
-
-  nsCOMPtr<nsIURI> uri;
-  rv = aQuery->GetSourceURI(getter_AddRefs(uri));
-  NS_ENSURE_SUCCESS(rv, rv);
-  // If the URI hasn't been set, bail
-  NS_ENSURE_STATE(uri);
-  nsCOMPtr<nsIPrincipal> principal;
-  // In nsIUrlClassifierDBService.lookup, the only use of the principal is to
-  // wrap the URI. nsISecurityManager.getNoAppCodebasePrincipal is the easiest
-  // way to wrap a URI inside a principal, since principals can't be
-  // constructed.
-  rv = mSecurityManager->GetNoAppCodebasePrincipal(uri,
-                                                   getter_AddRefs(principal));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // Check local lists to see if the URI has already been whitelisted or
-  // blacklisted.
-  return mDBService->Lookup(principal, lookup);
-}
--- a/toolkit/components/downloads/ApplicationReputation.h
+++ b/toolkit/components/downloads/ApplicationReputation.h
@@ -6,51 +6,93 @@
 
 #ifndef ApplicationReputation_h__
 #define ApplicationReputation_h__
 
 #include "nsIApplicationReputation.h"
 #include "nsIRequestObserver.h"
 #include "nsIStreamListener.h"
 #include "nsISupports.h"
-#include "nsIUrlClassifierDBService.h"
 
-#include "nsAutoPtr.h"
 #include "nsCOMPtr.h"
 #include "nsString.h"
 
+class nsIApplicationReputationListener;
+class nsIChannel;
+class nsIObserverService;
 class nsIRequest;
-class nsIUrlClassifierDBService;
-class nsIScriptSecurityManager;
-class PendingLookup;
+class PRLogModuleInfo;
 
 class ApplicationReputationService MOZ_FINAL :
   public nsIApplicationReputationService {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIAPPLICATIONREPUTATIONSERVICE
 
 public:
   static ApplicationReputationService* GetSingleton();
 
 private:
   /**
    * Global singleton object for holding this factory service.
    */
   static ApplicationReputationService* gApplicationReputationService;
-  /**
-   * Keeps track of services used to query the local database of URLs.
-   */
-  nsCOMPtr<nsIUrlClassifierDBService> mDBService;
-  nsCOMPtr<nsIScriptSecurityManager> mSecurityManager;
-  /**
-   * This is a singleton, so disallow construction.
-   */
+
   ApplicationReputationService();
   ~ApplicationReputationService();
+
   /**
    * Wrapper function for QueryReputation that makes it easier to ensure the
    * callback is called.
    */
   nsresult QueryReputationInternal(nsIApplicationReputationQuery* aQuery,
                                    nsIApplicationReputationCallback* aCallback);
 };
+
+/**
+ * This class implements nsIApplicationReputation. See the
+ * nsIApplicationReputation.idl for details. ApplicationReputation also
+ * implements nsIStreamListener because it calls nsIChannel->AsyncOpen.
+ */
+class ApplicationReputationQuery MOZ_FINAL :
+  public nsIApplicationReputationQuery,
+  public nsIStreamListener {
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIREQUESTOBSERVER
+  NS_DECL_NSISTREAMLISTENER
+  NS_DECL_NSIAPPLICATIONREPUTATIONQUERY
+
+  ApplicationReputationQuery();
+  ~ApplicationReputationQuery();
+
+private:
+  /**
+   * Corresponding member variables for the attributes in the IDL.
+   */
+  nsString mSuggestedFileName;
+  nsCOMPtr<nsIURI> mURI;
+  uint32_t mFileSize;
+  nsCString mSha256Hash;
+
+  /**
+   * The callback for the request.
+   */
+  nsCOMPtr<nsIApplicationReputationCallback> mCallback;
+
+  /**
+   * The response from the application reputation query. This is read in chunks
+   * as part of our nsIStreamListener implementation and may contain embedded
+   * NULLs.
+   */
+  nsCString mResponse;
+
+  /**
+   * Wrapper function for nsIStreamListener.onStopRequest to make it easy to
+   * guarantee calling the callback
+   */
+  nsresult OnStopRequestInternal(nsIRequest *aRequest,
+                                 nsISupports *aContext,
+                                 nsresult aResult,
+                                 bool* aShouldBlock);
+};
+
 #endif /* ApplicationReputation_h__ */
--- a/toolkit/components/downloads/nsIApplicationReputation.idl
+++ b/toolkit/components/downloads/nsIApplicationReputation.idl
@@ -38,42 +38,47 @@ interface nsIApplicationReputationServic
                        in nsIApplicationReputationCallback aCallback);
 };
 
 /**
  * A single-use, write-once interface for recording the metadata of the
  * downloaded file. nsIApplicationReputationService.Start() may only be called
  * once with a single query.
  */
-[scriptable, uuid(5a054991-e489-4a1c-a0aa-ea7c69b20e3d)]
+[scriptable, uuid(857da2c0-cfe5-11e2-8b8b-0800200c9a66)]
 interface nsIApplicationReputationQuery : nsISupports {
   /*
    * The nsIURI from which the file was downloaded. This may not be null.
    */
-  readonly attribute nsIURI sourceURI;
+  attribute nsIURI sourceURI;
 
   /*
    * The target filename for the downloaded file, as inferred from the source
    * URI or provided by the Content-Disposition attachment file name. If this
    * is not set by the caller, it will be passed as an empty string but the
    * query won't produce any useful information.
    */
-  readonly attribute AString suggestedFileName;
+  attribute AString suggestedFileName;
 
   /*
    * The size of the downloaded file in bytes.
    */
-  readonly attribute unsigned long fileSize;
+  attribute unsigned long fileSize;
 
   /*
    * The SHA256 hash of the downloaded file in raw bytes. If this is not set by
    * the caller, it will be passed as an empty string but the query won't
    * produce any useful information.
    */
-  readonly attribute ACString sha256Hash;
+  attribute ACString sha256Hash;
+
+  /**
+   * The callback object listening to this query.
+   */
+  attribute nsIApplicationReputationCallback callback;
 };
 
 [scriptable, function, uuid(9a228470-cfe5-11e2-8b8b-0800200c9a66)]
 interface nsIApplicationReputationCallback : nsISupports {
   /**
    * Callback for the result of the application reputation query.
    * @param aStatus
    *        NS_OK if and only if the query succeeded. If it did, then
deleted file mode 100644
--- a/toolkit/components/downloads/test/unit/data/digest.chunk
+++ /dev/null
@@ -1,2 +0,0 @@
-a:5:32:32
-_H^a7]=#nmnoQ
\ No newline at end of file
--- a/toolkit/components/downloads/test/unit/test_app_rep.js
+++ b/toolkit/components/downloads/test/unit/test_app_rep.js
@@ -2,57 +2,23 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* 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/. */
 
 Cu.import('resource://gre/modules/NetUtil.jsm');
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
+const ApplicationReputationQuery = Components.Constructor(
+      "@mozilla.org/downloads/application-reputation-query;1",
+      "nsIApplicationReputationQuery");
+
 const gAppRep = Cc["@mozilla.org/downloads/application-reputation-service;1"].
                   getService(Ci.nsIApplicationReputationService);
 let gHttpServ = null;
-let gTables = {};
-
-function readFileToString(aFilename) {
-  let f = do_get_file(aFilename);
-  let stream = Cc["@mozilla.org/network/file-input-stream;1"]
-                 .createInstance(Ci.nsIFileInputStream);
-  stream.init(f, -1, 0, 0);
-  let buf = NetUtil.readInputStreamToString(stream, stream.available());
-  return buf;
-}
-
-// Registers a table for which to serve update chunks. Returns a promise that
-// resolves when that chunk has been downloaded.
-function registerTableUpdate(aTable, aFilename) {
-  // If we haven't been given an update for this table yet, add it to the map
-  if (!(aTable in gTables)) {
-    gTables[aTable] = [];
-  }
-
-  // The number of chunks associated with this table.
-  let numChunks = gTables[aTable].length + 1;
-  let redirectPath = "/" + aTable + "-" + numChunks;
-  let redirectUrl = "localhost:4444" + redirectPath;
-
-  // Store redirect url for that table so we can return it later when we
-  // process an update request.
-  gTables[aTable].push(redirectUrl);
-
-  gHttpServ.registerPathHandler(redirectPath, function(request, response) {
-    do_print("Mock safebrowsing server handling request for " + redirectPath);
-    let contents = readFileToString(aFilename);
-    do_print("Length of " + aFilename + ": " + contents.length);
-    response.setHeader("Content-Type",
-                       "application/vnd.google.safebrowsing-update", false);
-    response.setStatusLine(request.httpVersion, 200, "OK");
-    response.bodyOutputStream.write(contents, contents.length);
-  });
-}
 
 function run_test() {
   // Set up a local HTTP server to return bad verdicts.
   Services.prefs.setCharPref("browser.safebrowsing.appRepURL",
                              "http://localhost:4444/download");
   // Ensure safebrowsing is enabled for this test, even if the app
   // doesn't have it enabled.
   Services.prefs.setBoolPref("browser.safebrowsing.malware.enabled", true);
@@ -78,164 +44,96 @@ function run_test() {
   }
 
   gHttpServ.registerPathHandler("/download", function(request, response) {
     response.setHeader("Content-Type", "application/octet-stream", false);
     let buf = NetUtil.readInputStreamToString(
       request.bodyInputStream,
       request.bodyInputStream.available());
     do_print("Request length: " + buf.length);
-    // A garbage response. By default this produces NS_CANNOT_CONVERT_DATA as
-    // the callback status.
+    // A garbage response.
     let blob = "this is not a serialized protocol buffer";
     // We can't actually parse the protocol buffer here, so just switch on the
     // length instead of inspecting the contents.
     if (buf.length == 35) {
-      // evil.com
       blob = createVerdict(true);
     } else if (buf.length == 38) {
-      // mozilla.com
       blob = createVerdict(false);
     }
     response.bodyOutputStream.write(blob, blob.length);
   });
 
   gHttpServ.start(4444);
 
   run_next_test();
 }
 
 add_test(function test_shouldBlock() {
-  gAppRep.queryReputation({
-    sourceURI: createURI("http://evil.com"),
-    fileSize: 12,
-  }, function onComplete(aShouldBlock, aStatus) {
+  let query = new ApplicationReputationQuery();
+  query.sourceURI = createURI("http://evil.com");
+  query.fileSize = 12;
+
+  gAppRep.queryReputation(query, function onComplete(aShouldBlock, aStatus) {
     do_check_true(aShouldBlock);
     do_check_eq(Cr.NS_OK, aStatus);
     run_next_test();
   });
 });
 
 add_test(function test_shouldNotBlock() {
-  gAppRep.queryReputation({
-    sourceURI: createURI("http://mozilla.com"),
-    fileSize: 12,
-  }, function onComplete(aShouldBlock, aStatus) {
+  let query = new ApplicationReputationQuery();
+  query.sourceURI = createURI("http://mozilla.com");
+  query.fileSize = 12;
+
+  gAppRep.queryReputation(query, function onComplete(aShouldBlock, aStatus) {
     do_check_eq(Cr.NS_OK, aStatus);
     do_check_false(aShouldBlock);
     run_next_test();
   });
 });
 
+add_test(function test_garbage() {
+  let query = new ApplicationReputationQuery();
+  query.sourceURI = createURI("http://thisisagarbageurl.com");
+  query.fileSize = 12;
+
+  gAppRep.queryReputation(query, function onComplete(aShouldBlock, aStatus) {
+    // We should be getting the garbage response.
+    do_check_eq(Cr.NS_ERROR_CANNOT_CONVERT_DATA, aStatus);
+    do_check_false(aShouldBlock);
+    run_next_test();
+  });
+});
+
 add_test(function test_nullSourceURI() {
-  gAppRep.queryReputation({
-    // No source URI
-    fileSize: 12,
-  }, function onComplete(aShouldBlock, aStatus) {
+  let query = new ApplicationReputationQuery();
+  query.fileSize = 12;
+  // No source URI
+  gAppRep.queryReputation(query, function onComplete(aShouldBlock, aStatus) {
     do_check_eq(Cr.NS_ERROR_UNEXPECTED, aStatus);
     do_check_false(aShouldBlock);
     run_next_test();
   });
 });
 
 add_test(function test_nullCallback() {
+  let query = new ApplicationReputationQuery();
+  query.fileSize = 12;
   try {
-    gAppRep.queryReputation({
-      sourceURI: createURI("http://example.com"),
-      fileSize: 12,
-    }, null);
+    gAppRep.queryReputation(query, null);
     do_throw("Callback cannot be null");
   } catch (ex if ex.result == Cr.NS_ERROR_INVALID_POINTER) {
     run_next_test();
   }
 });
 
 add_test(function test_disabled() {
   Services.prefs.setCharPref("browser.safebrowsing.appRepURL", "");
-  gAppRep.queryReputation({
-    sourceURI: createURI("http://example.com"),
-    fileSize: 12,
-  }, function onComplete(aShouldBlock, aStatus) {
+  let query = new ApplicationReputationQuery();
+  query.sourceURI = createURI("http://example.com");
+  query.fileSize = 12;
+  gAppRep.queryReputation(query, function onComplete(aShouldBlock, aStatus) {
     // We should be getting NS_ERROR_NOT_AVAILABLE if the service is disabled
     do_check_eq(Cr.NS_ERROR_NOT_AVAILABLE, aStatus);
     do_check_false(aShouldBlock);
     run_next_test();
   });
 });
-
-add_test(function test_garbage() {
-  Services.prefs.setCharPref("browser.safebrowsing.appRepURL",
-                             "http://localhost:4444/download");
-  gAppRep.queryReputation({
-    sourceURI: createURI("http://whitelisted.com"),
-    fileSize: 12,
-  }, function onComplete(aShouldBlock, aStatus) {
-    // We should be getting the garbage response.
-    do_check_eq(Cr.NS_ERROR_CANNOT_CONVERT_DATA, aStatus);
-    do_check_false(aShouldBlock);
-    run_next_test();
-  });
-});
-
-// Set up the local whitelist.
-add_test(function test_local_list() {
-  // Construct a response with redirect urls.
-  function processUpdateRequest() {
-    let response = "n:1000\n";
-    for (let table in gTables) {
-      response += "i:" + table + "\n";
-      for (let i = 0; i < gTables[table].length; ++i) {
-        response += "u:" + gTables[table][i] + "\n";
-      }
-    }
-    do_print("Returning update response: " + response);
-    return response;
-  }
-  gHttpServ.registerPathHandler("/downloads", function(request, response) {
-    let buf = NetUtil.readInputStreamToString(request.bodyInputStream,
-      request.bodyInputStream.available());
-    let blob = processUpdateRequest();
-    response.setHeader("Content-Type",
-                       "application/vnd.google.safebrowsing-update", false);
-    response.setStatusLine(request.httpVersion, 200, "OK");
-    response.bodyOutputStream.write(blob, blob.length);
-  });
-
-  let streamUpdater = Cc["@mozilla.org/url-classifier/streamupdater;1"]
-    .getService(Ci.nsIUrlClassifierStreamUpdater);
-  streamUpdater.updateUrl = "http://localhost:4444/downloads";
-
-  // Load up some update chunks for the safebrowsing server to serve. This
-  // particular chunk contains the hash of whitelisted.com/.
-  registerTableUpdate("goog-downloadwhite-digest256", "data/digest.chunk");
-
-  // Download some updates, and don't continue until the downloads are done.
-  function updateSuccess(aEvent) {
-    // Timeout of n:1000 is constructed in processUpdateRequest above and
-    // passed back in the callback in nsIUrlClassifierStreamUpdater on success.
-    do_check_eq("1000", aEvent);
-    do_print("All data processed");
-    run_next_test();
-  }
-  // Just throw if we ever get an update or download error.
-  function handleError(aEvent) {
-    do_throw("We didn't download or update correctly: " + aEvent);
-  }
-  streamUpdater.downloadUpdates(
-    "goog-downloadwhite-digest256",
-    "goog-downloadwhite-digest256;\n", "",
-    updateSuccess, handleError, handleError);
-});
-
-// After being whitelisted, we shouldn't throw.
-add_test(function test_local_whitelist() {
-  Services.prefs.setCharPref("browser.safebrowsing.appRepURL",
-                             "http://localhost:4444/download");
-  gAppRep.queryReputation({
-    sourceURI: createURI("http://whitelisted.com"),
-    fileSize: 12,
-  }, function onComplete(aShouldBlock, aStatus) {
-    // We would get garbage if this query made it to the remote server.
-    do_check_eq(Cr.NS_OK, aStatus);
-    do_check_false(aShouldBlock);
-    run_next_test();
-  });
-});
--- a/toolkit/components/url-classifier/SafeBrowsing.jsm
+++ b/toolkit/components/url-classifier/SafeBrowsing.jsm
@@ -5,22 +5,18 @@
 this.EXPORTED_SYMBOLS = ["SafeBrowsing"];
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 
 Cu.import("resource://gre/modules/Services.jsm");
 
-const phishingList = Services.prefs.getCharPref("urlclassifier.phish_table");
-const malwareList = Services.prefs.getCharPref("urlclassifier.malware_table");
-const downloadBlockList =
-  Services.prefs.getCharPref("urlclassifier.download_block_table");
-const downloadAllowList =
-  Services.prefs.getCharPref("urlclassifier.download_allow_table");
+const [phishingList, malwareList] =
+  Services.prefs.getCharPref("urlclassifier.gethashtables").split(",").map(function(value) value.trim());
 
 var debug = false;
 function log(...stuff) {
   if (!debug)
     return;
 
   let msg = "SafeBrowsing: " + stuff.join(" ");
   Services.console.logStringMessage(msg);
@@ -38,18 +34,16 @@ this.SafeBrowsing = {
     Services.prefs.addObserver("browser.safebrowsing", this.readPrefs, false);
     this.readPrefs();
 
     // Register our two types of tables, and add custom Mozilla entries
     let listManager = Cc["@mozilla.org/url-classifier/listmanager;1"].
                       getService(Ci.nsIUrlListManager);
     listManager.registerTable(phishingList, false);
     listManager.registerTable(malwareList, false);
-    listManager.registerTable(downloadBlockList, false);
-    listManager.registerTable(downloadAllowList, false);
     this.addMozEntries();
 
     this.controlUpdateChecking();
     this.initialized = true;
 
     log("init() finished");
   },
 
--- a/toolkit/components/url-classifier/moz.build
+++ b/toolkit/components/url-classifier/moz.build
@@ -43,20 +43,14 @@ EXTRA_PP_COMPONENTS += [
 ]
 
 LIBRARY_NAME = 'urlclassifier_s'
 
 EXTRA_JS_MODULES += [
     'SafeBrowsing.jsm',
 ]
 
-EXPORTS += [
-    'Entries.h',
-    'LookupCache.h',
-    'nsUrlClassifierPrefixSet.h'
-]
-
 FAIL_ON_WARNINGS = True
 
 LIBXUL_LIBRARY = True
 
 MSVC_ENABLE_PGO = True
 
--- a/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp
+++ b/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp
@@ -28,17 +28,16 @@
 #include "nsNetUtil.h"
 #include "nsNetCID.h"
 #include "nsThreadUtils.h"
 #include "nsXPCOMStrings.h"
 #include "nsProxyRelease.h"
 #include "mozilla/Atomics.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/Mutex.h"
-#include "mozilla/Preferences.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/Telemetry.h"
 #include "prlog.h"
 #include "prprf.h"
 #include "prnetdb.h"
 #include "Entries.h"
 #include "mozilla/Attributes.h"
 #include "nsIPrincipal.h"
@@ -64,20 +63,17 @@ PRLogModuleInfo *gUrlClassifierDbService
 #define CHECK_MALWARE_DEFAULT   false
 
 #define CHECK_PHISHING_PREF     "browser.safebrowsing.enabled"
 #define CHECK_PHISHING_DEFAULT  false
 
 #define GETHASH_NOISE_PREF      "urlclassifier.gethashnoise"
 #define GETHASH_NOISE_DEFAULT   4
 
-#define MALWARE_TABLE_PREF      "urlclassifier.malware_table"
-#define PHISH_TABLE_PREF        "urlclassifier.phish_table"
-#define DOWNLOAD_BLOCK_TABLE_PREF "urlclassifier.download_block_table"
-#define DOWNLOAD_ALLOW_TABLE_PREF "urlclassifier.download_allow_table"
+#define GETHASH_TABLES_PREF     "urlclassifier.gethashtables"
 
 #define CONFIRM_AGE_PREF        "urlclassifier.max-complete-age"
 #define CONFIRM_AGE_DEFAULT_SEC (45 * 60)
 
 class nsUrlClassifierDBServiceWorker;
 
 // Singleton instance.
 static nsUrlClassifierDBService* sUrlClassifierDBService;
@@ -1114,44 +1110,53 @@ nsUrlClassifierDBService::~nsUrlClassifi
 nsresult
 nsUrlClassifierDBService::Init()
 {
 #if defined(PR_LOGGING)
   if (!gUrlClassifierDbServiceLog)
     gUrlClassifierDbServiceLog = PR_NewLogModule("UrlClassifierDbService");
 #endif
 
-  // Retrieve all the preferences.
-  mCheckMalware = Preferences::GetBool(CHECK_MALWARE_PREF,
-    CHECK_MALWARE_DEFAULT);
-  mCheckPhishing = Preferences::GetBool(CHECK_PHISHING_PREF,
-    CHECK_PHISHING_DEFAULT);
-  uint32_t gethashNoise = Preferences::GetUint(GETHASH_NOISE_PREF,
-    GETHASH_NOISE_DEFAULT);
-  gFreshnessGuarantee = Preferences::GetInt(CONFIRM_AGE_PREF,
-    CONFIRM_AGE_DEFAULT_SEC);
-  mGethashTables.AppendElement(Preferences::GetCString(PHISH_TABLE_PREF));
-  mGethashTables.AppendElement(Preferences::GetCString(MALWARE_TABLE_PREF));
-  mGethashTables.AppendElement(Preferences::GetCString(
-    DOWNLOAD_BLOCK_TABLE_PREF));
-  mGethashTables.AppendElement(Preferences::GetCString(
-    DOWNLOAD_ALLOW_TABLE_PREF));
+  nsresult rv;
+
+  // Should we check document loads for malware URIs?
+  nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
+
+  uint32_t gethashNoise = 0;
+  if (prefs) {
+    bool tmpbool;
+    rv = prefs->GetBoolPref(CHECK_MALWARE_PREF, &tmpbool);
+    mCheckMalware = NS_SUCCEEDED(rv) ? tmpbool : CHECK_MALWARE_DEFAULT;
+
+    prefs->AddObserver(CHECK_MALWARE_PREF, this, false);
+
+    rv = prefs->GetBoolPref(CHECK_PHISHING_PREF, &tmpbool);
+    mCheckPhishing = NS_SUCCEEDED(rv) ? tmpbool : CHECK_PHISHING_DEFAULT;
+
+    prefs->AddObserver(CHECK_PHISHING_PREF, this, false);
 
-  // Do we *really* need to be able to change all of these at runtime?
-  Preferences::AddStrongObserver(this, CHECK_MALWARE_PREF);
-  Preferences::AddStrongObserver(this, CHECK_PHISHING_PREF);
-  Preferences::AddStrongObserver(this, GETHASH_NOISE_PREF);
-  Preferences::AddStrongObserver(this, CONFIRM_AGE_PREF);
-  Preferences::AddStrongObserver(this, PHISH_TABLE_PREF);
-  Preferences::AddStrongObserver(this, MALWARE_TABLE_PREF);
-  Preferences::AddStrongObserver(this, DOWNLOAD_BLOCK_TABLE_PREF);
-  Preferences::AddStrongObserver(this, DOWNLOAD_ALLOW_TABLE_PREF);
+    int32_t tmpint;
+    rv = prefs->GetIntPref(GETHASH_NOISE_PREF, &tmpint);
+    gethashNoise = (NS_SUCCEEDED(rv) && tmpint >= 0) ?
+      static_cast<uint32_t>(tmpint) : GETHASH_NOISE_DEFAULT;
+
+    nsXPIDLCString tmpstr;
+    if (NS_SUCCEEDED(prefs->GetCharPref(GETHASH_TABLES_PREF, getter_Copies(tmpstr)))) {
+      SplitTables(tmpstr, mGethashWhitelist);
+    }
+
+    prefs->AddObserver(GETHASH_TABLES_PREF, this, false);
+
+    rv = prefs->GetIntPref(CONFIRM_AGE_PREF, &tmpint);
+    gFreshnessGuarantee = NS_SUCCEEDED(rv) ? tmpint : CONFIRM_AGE_DEFAULT_SEC;
+
+    prefs->AddObserver(CONFIRM_AGE_PREF, this, false);
+  }
 
   // Force PSM loading on main thread
-  nsresult rv;
   nsCOMPtr<nsICryptoHash> acryptoHash = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Directory providers must also be accessed on the main thread.
   nsCOMPtr<nsIFile> cacheDir;
   rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR,
                               getter_AddRefs(cacheDir));
   if (NS_FAILED(rv)) {
@@ -1419,53 +1424,50 @@ nsUrlClassifierDBService::CacheMisses(Pr
 bool
 nsUrlClassifierDBService::GetCompleter(const nsACString &tableName,
                                        nsIUrlClassifierHashCompleter **completer)
 {
   if (mCompleters.Get(tableName, completer)) {
     return true;
   }
 
-  if (!mGethashTables.Contains(tableName)) {
+  if (!mGethashWhitelist.Contains(tableName)) {
     return false;
   }
 
   return NS_SUCCEEDED(CallGetService(NS_URLCLASSIFIERHASHCOMPLETER_CONTRACTID,
                                      completer));
 }
 
 NS_IMETHODIMP
 nsUrlClassifierDBService::Observe(nsISupports *aSubject, const char *aTopic,
                                   const PRUnichar *aData)
 {
   if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
     nsresult rv;
     nsCOMPtr<nsIPrefBranch> prefs(do_QueryInterface(aSubject, &rv));
     NS_ENSURE_SUCCESS(rv, rv);
     if (NS_LITERAL_STRING(CHECK_MALWARE_PREF).Equals(aData)) {
-      mCheckMalware = Preferences::GetBool(CHECK_MALWARE_PREF,
-        CHECK_MALWARE_DEFAULT);
+      bool tmpbool;
+      rv = prefs->GetBoolPref(CHECK_MALWARE_PREF, &tmpbool);
+      mCheckMalware = NS_SUCCEEDED(rv) ? tmpbool : CHECK_MALWARE_DEFAULT;
     } else if (NS_LITERAL_STRING(CHECK_PHISHING_PREF).Equals(aData)) {
-      mCheckPhishing = Preferences::GetBool(CHECK_PHISHING_PREF,
-        CHECK_PHISHING_DEFAULT);
-    } else if (NS_LITERAL_STRING(PHISH_TABLE_PREF).Equals(aData) ||
-               NS_LITERAL_STRING(MALWARE_TABLE_PREF).Equals(aData) ||
-               NS_LITERAL_STRING(DOWNLOAD_BLOCK_TABLE_PREF).Equals(aData) ||
-               NS_LITERAL_STRING(DOWNLOAD_ALLOW_TABLE_PREF).Equals(aData)) {
-      // Just read everything again.
-      mGethashTables.Clear();
-      mGethashTables.AppendElement(Preferences::GetCString(PHISH_TABLE_PREF));
-      mGethashTables.AppendElement(Preferences::GetCString(MALWARE_TABLE_PREF));
-      mGethashTables.AppendElement(Preferences::GetCString(
-        DOWNLOAD_BLOCK_TABLE_PREF));
-      mGethashTables.AppendElement(Preferences::GetCString(
-        DOWNLOAD_ALLOW_TABLE_PREF));
+      bool tmpbool;
+      rv = prefs->GetBoolPref(CHECK_PHISHING_PREF, &tmpbool);
+      mCheckPhishing = NS_SUCCEEDED(rv) ? tmpbool : CHECK_PHISHING_DEFAULT;
+    } else if (NS_LITERAL_STRING(GETHASH_TABLES_PREF).Equals(aData)) {
+      mGethashWhitelist.Clear();
+      nsXPIDLCString val;
+      if (NS_SUCCEEDED(prefs->GetCharPref(GETHASH_TABLES_PREF, getter_Copies(val)))) {
+        SplitTables(val, mGethashWhitelist);
+      }
     } else if (NS_LITERAL_STRING(CONFIRM_AGE_PREF).Equals(aData)) {
-      gFreshnessGuarantee = Preferences::GetInt(CONFIRM_AGE_PREF,
-        CONFIRM_AGE_DEFAULT_SEC);
+      int32_t tmpint;
+      rv = prefs->GetIntPref(CONFIRM_AGE_PREF, &tmpint);
+      gFreshnessGuarantee = NS_SUCCEEDED(rv) ? tmpint : CONFIRM_AGE_DEFAULT_SEC;
     }
   } else if (!strcmp(aTopic, "profile-before-change") ||
              !strcmp(aTopic, "xpcom-shutdown-threads")) {
     Shutdown();
   } else {
     return NS_ERROR_UNEXPECTED;
   }
 
@@ -1482,20 +1484,17 @@ nsUrlClassifierDBService::Shutdown()
     return NS_OK;
 
   mCompleters.Clear();
 
   nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
   if (prefs) {
     prefs->RemoveObserver(CHECK_MALWARE_PREF, this);
     prefs->RemoveObserver(CHECK_PHISHING_PREF, this);
-    prefs->RemoveObserver(PHISH_TABLE_PREF, this);
-    prefs->RemoveObserver(MALWARE_TABLE_PREF, this);
-    prefs->RemoveObserver(DOWNLOAD_BLOCK_TABLE_PREF, this);
-    prefs->RemoveObserver(DOWNLOAD_ALLOW_TABLE_PREF, this);
+    prefs->RemoveObserver(GETHASH_TABLES_PREF, this);
     prefs->RemoveObserver(CONFIRM_AGE_PREF, this);
   }
 
   DebugOnly<nsresult> rv;
   // First close the db connection.
   if (mWorker) {
     rv = mWorkerProxy->CancelUpdate();
     NS_ASSERTION(NS_SUCCEEDED(rv), "failed to post cancel update event");
--- a/toolkit/components/url-classifier/nsUrlClassifierDBService.h
+++ b/toolkit/components/url-classifier/nsUrlClassifierDBService.h
@@ -96,17 +96,17 @@ private:
 
   // TRUE if a BeginUpdate() has been called without an accompanying
   // CancelUpdate()/FinishUpdate().  This is used to prevent competing
   // updates, not to determine whether an update is still being
   // processed.
   bool mInUpdate;
 
   // The list of tables that can use the default hash completer object.
-  nsTArray<nsCString> mGethashTables;
+  nsTArray<nsCString> mGethashWhitelist;
 
   // Thread that we do the updates on.
   static nsIThread* gDbBackgroundThread;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsUrlClassifierDBService, NS_URLCLASSIFIERDBSERVICE_CID)
 
 #endif // nsUrlClassifierDBService_h_