LCOV - code coverage report
Current view: top level - toolkit/components/url-classifier - nsUrlClassifierDBService.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 314 1011 31.1 %
Date: 2017-07-14 16:53:18 Functions: 43 105 41.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : //* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* This Source Code Form is subject to the terms of the Mozilla Public
       3             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       4             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       5             : 
       6             : #include "nsAutoPtr.h"
       7             : #include "nsCOMPtr.h"
       8             : #include "nsAppDirectoryServiceDefs.h"
       9             : #include "nsArrayUtils.h"
      10             : #include "nsCRT.h"
      11             : #include "nsICryptoHash.h"
      12             : #include "nsICryptoHMAC.h"
      13             : #include "nsIDirectoryService.h"
      14             : #include "nsIKeyModule.h"
      15             : #include "nsIObserverService.h"
      16             : #include "nsIPermissionManager.h"
      17             : #include "nsIPrefBranch.h"
      18             : #include "nsIPrefService.h"
      19             : #include "nsIProperties.h"
      20             : #include "nsToolkitCompsCID.h"
      21             : #include "nsIXULRuntime.h"
      22             : #include "nsUrlClassifierDBService.h"
      23             : #include "nsUrlClassifierUtils.h"
      24             : #include "nsUrlClassifierProxies.h"
      25             : #include "nsURILoader.h"
      26             : #include "nsString.h"
      27             : #include "nsReadableUtils.h"
      28             : #include "nsTArray.h"
      29             : #include "nsNetCID.h"
      30             : #include "nsThreadUtils.h"
      31             : #include "nsProxyRelease.h"
      32             : #include "nsString.h"
      33             : #include "mozilla/Atomics.h"
      34             : #include "mozilla/DebugOnly.h"
      35             : #include "mozilla/ErrorNames.h"
      36             : #include "mozilla/Mutex.h"
      37             : #include "mozilla/Preferences.h"
      38             : #include "mozilla/SizePrintfMacros.h"
      39             : #include "mozilla/TimeStamp.h"
      40             : #include "mozilla/Telemetry.h"
      41             : #include "mozilla/Logging.h"
      42             : #include "prnetdb.h"
      43             : #include "Entries.h"
      44             : #include "HashStore.h"
      45             : #include "Classifier.h"
      46             : #include "ProtocolParser.h"
      47             : #include "mozilla/Attributes.h"
      48             : #include "nsIPrincipal.h"
      49             : #include "Classifier.h"
      50             : #include "ProtocolParser.h"
      51             : #include "nsContentUtils.h"
      52             : #include "mozilla/dom/ContentChild.h"
      53             : #include "mozilla/dom/PermissionMessageUtils.h"
      54             : #include "mozilla/dom/URLClassifierChild.h"
      55             : #include "mozilla/ipc/URIUtils.h"
      56             : #include "nsProxyRelease.h"
      57             : 
      58             : namespace mozilla {
      59             : namespace safebrowsing {
      60             : 
      61             : nsresult
      62           0 : TablesToResponse(const nsACString& tables)
      63             : {
      64           0 :   if (tables.IsEmpty()) {
      65           0 :     return NS_OK;
      66             :   }
      67             : 
      68             :   // We don't check mCheckMalware and friends because disabled tables are
      69             :   // never included
      70           0 :   if (FindInReadable(NS_LITERAL_CSTRING("-malware-"), tables)) {
      71           0 :     return NS_ERROR_MALWARE_URI;
      72             :   }
      73           0 :   if (FindInReadable(NS_LITERAL_CSTRING("-phish-"), tables)) {
      74           0 :     return NS_ERROR_PHISHING_URI;
      75             :   }
      76           0 :   if (FindInReadable(NS_LITERAL_CSTRING("-unwanted-"), tables)) {
      77           0 :     return NS_ERROR_UNWANTED_URI;
      78             :   }
      79           0 :   if (FindInReadable(NS_LITERAL_CSTRING("-track-"), tables)) {
      80           0 :     return NS_ERROR_TRACKING_URI;
      81             :   }
      82           0 :   if (FindInReadable(NS_LITERAL_CSTRING("-block-"), tables)) {
      83           0 :     return NS_ERROR_BLOCKED_URI;
      84             :   }
      85           0 :   return NS_OK;
      86             : }
      87             : 
      88             : } // namespace safebrowsing
      89             : } // namespace mozilla
      90             : 
      91             : using namespace mozilla;
      92             : using namespace mozilla::safebrowsing;
      93             : 
      94             : // MOZ_LOG=UrlClassifierDbService:5
      95             : LazyLogModule gUrlClassifierDbServiceLog("UrlClassifierDbService");
      96             : #define LOG(args) MOZ_LOG(gUrlClassifierDbServiceLog, mozilla::LogLevel::Debug, args)
      97             : #define LOG_ENABLED() MOZ_LOG_TEST(gUrlClassifierDbServiceLog, mozilla::LogLevel::Debug)
      98             : 
      99             : #define GETHASH_NOISE_PREF      "urlclassifier.gethashnoise"
     100             : #define GETHASH_NOISE_DEFAULT   4
     101             : 
     102             : // 30 minutes as the maximum negative cache duration.
     103             : #define MAXIMUM_NEGATIVE_CACHE_DURATION_SEC (30 * 60 * 1000)
     104             : 
     105             : class nsUrlClassifierDBServiceWorker;
     106             : 
     107             : // Singleton instance.
     108             : static nsUrlClassifierDBService* sUrlClassifierDBService;
     109             : 
     110             : nsIThread* nsUrlClassifierDBService::gDbBackgroundThread = nullptr;
     111             : 
     112             : // Once we've committed to shutting down, don't do work in the background
     113             : // thread.
     114             : static bool gShuttingDownThread = false;
     115             : 
     116             : static uint32_t sGethashNoise = GETHASH_NOISE_DEFAULT;
     117             : 
     118          38 : NS_IMPL_ISUPPORTS(nsUrlClassifierDBServiceWorker,
     119             :                   nsIUrlClassifierDBService)
     120             : 
     121           1 : nsUrlClassifierDBServiceWorker::nsUrlClassifierDBServiceWorker()
     122             :   : mInStream(false)
     123             :   , mGethashNoise(0)
     124           1 :   , mPendingLookupLock("nsUrlClassifierDBServerWorker.mPendingLookupLock")
     125             : {
     126           1 : }
     127             : 
     128           0 : nsUrlClassifierDBServiceWorker::~nsUrlClassifierDBServiceWorker()
     129             : {
     130           0 :   NS_ASSERTION(!mClassifier,
     131             :                "Db connection not closed, leaking memory!  Call CloseDb "
     132             :                "to close the connection.");
     133           0 : }
     134             : 
     135             : nsresult
     136           1 : nsUrlClassifierDBServiceWorker::Init(uint32_t aGethashNoise,
     137             :                                      nsCOMPtr<nsIFile> aCacheDir,
     138             :                                      nsUrlClassifierDBService *aDBService)
     139             : {
     140           1 :   mGethashNoise = aGethashNoise;
     141           1 :   mCacheDir = aCacheDir;
     142           1 :   mDBService = aDBService;
     143             : 
     144           1 :   ResetUpdate();
     145             : 
     146           1 :   return NS_OK;
     147             : }
     148             : 
     149             : nsresult
     150           0 : nsUrlClassifierDBServiceWorker::QueueLookup(const nsACString& spec,
     151             :                                             const nsACString& tables,
     152             :                                             nsIUrlClassifierLookupCallback* callback)
     153             : {
     154           0 :   MutexAutoLock lock(mPendingLookupLock);
     155           0 :   if (gShuttingDownThread) {
     156           0 :       return NS_ERROR_ABORT;
     157             :   }
     158             : 
     159           0 :   PendingLookup* lookup = mPendingLookups.AppendElement();
     160           0 :   if (!lookup) return NS_ERROR_OUT_OF_MEMORY;
     161             : 
     162           0 :   lookup->mStartTime = TimeStamp::Now();
     163           0 :   lookup->mKey = spec;
     164           0 :   lookup->mCallback = callback;
     165           0 :   lookup->mTables = tables;
     166             : 
     167           0 :   return NS_OK;
     168             : }
     169             : 
     170             : nsresult
     171           1 : nsUrlClassifierDBServiceWorker::DoLocalLookup(const nsACString& spec,
     172             :                                               const nsACString& tables,
     173             :                                               LookupResultArray* results)
     174             : {
     175           1 :   if (gShuttingDownThread) {
     176           0 :     return NS_ERROR_ABORT;
     177             :   }
     178             : 
     179           1 :   MOZ_ASSERT(!NS_IsMainThread(), "DoLocalLookup must be on background thread");
     180           1 :   if (!results) {
     181           0 :     return NS_ERROR_FAILURE;
     182             :   }
     183             :   // Bail if we haven't been initialized on the background thread.
     184           1 :   if (!mClassifier) {
     185           0 :     return NS_ERROR_NOT_AVAILABLE;
     186             :   }
     187             : 
     188             :   // We ignore failures from Check because we'd rather return the
     189             :   // results that were found than fail.
     190           1 :   mClassifier->Check(spec, tables, *results);
     191             : 
     192           1 :   LOG(("Found %" PRIuSIZE " results.", results->Length()));
     193           1 :   return NS_OK;
     194             : }
     195             : 
     196             : static nsresult
     197           0 : ProcessLookupResults(LookupResultArray* results, nsTArray<nsCString>& tables)
     198             : {
     199             :   // Build the result array.
     200           0 :   for (uint32_t i = 0; i < results->Length(); i++) {
     201           0 :     LookupResult& result = results->ElementAt(i);
     202           0 :     MOZ_ASSERT(!result.mNoise, "Lookup results should not have noise added");
     203           0 :     LOG(("Found result from table %s", result.mTableName.get()));
     204           0 :     if (tables.IndexOf(result.mTableName) == nsTArray<nsCString>::NoIndex) {
     205           0 :       tables.AppendElement(result.mTableName);
     206             :     }
     207             :   }
     208           0 :   return NS_OK;
     209             : }
     210             : 
     211             : /**
     212             :  * Lookup up a key in the database is a two step process:
     213             :  *
     214             :  * a) First we look for any Entries in the database that might apply to this
     215             :  *    url.  For each URL there are one or two possible domain names to check:
     216             :  *    the two-part domain name (example.com) and the three-part name
     217             :  *    (www.example.com).  We check the database for both of these.
     218             :  * b) If we find any entries, we check the list of fragments for that entry
     219             :  *    against the possible subfragments of the URL as described in the
     220             :  *    "Simplified Regular Expression Lookup" section of the protocol doc.
     221             :  */
     222             : nsresult
     223           0 : nsUrlClassifierDBServiceWorker::DoLookup(const nsACString& spec,
     224             :                                          const nsACString& tables,
     225             :                                          nsIUrlClassifierLookupCallback* c)
     226             : {
     227           0 :   if (gShuttingDownThread) {
     228           0 :     c->LookupComplete(nullptr);
     229           0 :     return NS_ERROR_NOT_INITIALIZED;
     230             :   }
     231             : 
     232           0 :   PRIntervalTime clockStart = 0;
     233           0 :   if (LOG_ENABLED()) {
     234           0 :     clockStart = PR_IntervalNow();
     235             :   }
     236             : 
     237           0 :   nsAutoPtr<LookupResultArray> results(new LookupResultArray());
     238           0 :   if (!results) {
     239           0 :     c->LookupComplete(nullptr);
     240           0 :     return NS_ERROR_OUT_OF_MEMORY;
     241             :   }
     242             : 
     243           0 :   nsresult rv = DoLocalLookup(spec, tables, results);
     244           0 :   if (NS_FAILED(rv)) {
     245           0 :     c->LookupComplete(nullptr);
     246           0 :     return rv;
     247             :   }
     248             : 
     249           0 :   LOG(("Found %" PRIuSIZE " results.", results->Length()));
     250             : 
     251             : 
     252           0 :   if (LOG_ENABLED()) {
     253           0 :     PRIntervalTime clockEnd = PR_IntervalNow();
     254           0 :     LOG(("query took %dms\n",
     255             :          PR_IntervalToMilliseconds(clockEnd - clockStart)));
     256             :   }
     257             : 
     258           0 :   for (uint32_t i = 0; i < results->Length(); i++) {
     259           0 :     const LookupResult& lookupResult = results->ElementAt(i);
     260             : 
     261           0 :     if (!lookupResult.Confirmed() &&
     262           0 :         mDBService->CanComplete(lookupResult.mTableName)) {
     263             : 
     264             :       // We're going to be doing a gethash request, add some extra entries.
     265             :       // Note that we cannot pass the first two by reference, because we
     266             :       // add to completes, whicah can cause completes to reallocate and move.
     267           0 :       AddNoise(lookupResult.hash.fixedLengthPrefix,
     268             :                lookupResult.mTableName,
     269           0 :                mGethashNoise, *results);
     270           0 :       break;
     271             :     }
     272             :   }
     273             : 
     274             :   // At this point ownership of 'results' is handed to the callback.
     275           0 :   c->LookupComplete(results.forget());
     276             : 
     277           0 :   return NS_OK;
     278             : }
     279             : 
     280             : nsresult
     281           1 : nsUrlClassifierDBServiceWorker::HandlePendingLookups()
     282             : {
     283           1 :   if (gShuttingDownThread) {
     284           0 :     return NS_ERROR_ABORT;
     285             :   }
     286             : 
     287           2 :   MutexAutoLock lock(mPendingLookupLock);
     288           1 :   while (mPendingLookups.Length() > 0) {
     289           0 :     PendingLookup lookup = mPendingLookups[0];
     290           0 :     mPendingLookups.RemoveElementAt(0);
     291             :     {
     292           0 :       MutexAutoUnlock unlock(mPendingLookupLock);
     293           0 :       DoLookup(lookup.mKey, lookup.mTables, lookup.mCallback);
     294             :     }
     295           0 :     double lookupTime = (TimeStamp::Now() - lookup.mStartTime).ToMilliseconds();
     296           0 :     Telemetry::Accumulate(Telemetry::URLCLASSIFIER_LOOKUP_TIME_2,
     297           0 :                           static_cast<uint32_t>(lookupTime));
     298             :   }
     299             : 
     300           1 :   return NS_OK;
     301             : }
     302             : 
     303             : nsresult
     304           0 : nsUrlClassifierDBServiceWorker::AddNoise(const Prefix aPrefix,
     305             :                                          const nsCString tableName,
     306             :                                          uint32_t aCount,
     307             :                                          LookupResultArray& results)
     308             : {
     309           0 :   if (gShuttingDownThread) {
     310           0 :     return NS_ERROR_ABORT;
     311             :   }
     312             : 
     313           0 :   if (aCount < 1) {
     314           0 :     return NS_OK;
     315             :   }
     316             : 
     317           0 :   PrefixArray noiseEntries;
     318           0 :   nsresult rv = mClassifier->ReadNoiseEntries(aPrefix, tableName,
     319           0 :                                               aCount, &noiseEntries);
     320           0 :   NS_ENSURE_SUCCESS(rv, rv);
     321             : 
     322           0 :   for (uint32_t i = 0; i < noiseEntries.Length(); i++) {
     323           0 :     LookupResult *result = results.AppendElement();
     324           0 :     if (!result)
     325           0 :       return NS_ERROR_OUT_OF_MEMORY;
     326             : 
     327           0 :     result->hash.fixedLengthPrefix = noiseEntries[i];
     328           0 :     result->mNoise = true;
     329           0 :     result->mPartialHashLength = PREFIX_SIZE; // Noise is always 4-byte,
     330           0 :     result->mTableName.Assign(tableName);
     331             :   }
     332             : 
     333           0 :   return NS_OK;
     334             : }
     335             : 
     336             : // Lookup a key in the db.
     337             : NS_IMETHODIMP
     338           0 : nsUrlClassifierDBServiceWorker::Lookup(nsIPrincipal* aPrincipal,
     339             :                                        const nsACString& aTables,
     340             :                                        nsIUrlClassifierCallback* c)
     341             : {
     342           0 :   if (gShuttingDownThread) {
     343           0 :     return NS_ERROR_ABORT;
     344             :   }
     345             : 
     346           0 :   return HandlePendingLookups();
     347             : }
     348             : 
     349             : NS_IMETHODIMP
     350           2 : nsUrlClassifierDBServiceWorker::GetTables(nsIUrlClassifierCallback* c)
     351             : {
     352           2 :   if (gShuttingDownThread) {
     353           0 :     return NS_ERROR_NOT_INITIALIZED;
     354             :   }
     355             : 
     356           2 :   nsresult rv = OpenDb();
     357           2 :   if (NS_FAILED(rv)) {
     358           0 :     NS_ERROR("Unable to open SafeBrowsing database");
     359           0 :     return NS_ERROR_FAILURE;
     360             :   }
     361             : 
     362           2 :   NS_ENSURE_SUCCESS(rv, rv);
     363             : 
     364           4 :   nsAutoCString response;
     365           2 :   mClassifier->TableRequest(response);
     366           2 :   LOG(("GetTables: %s", response.get()));
     367           2 :   c->HandleEvent(response);
     368             : 
     369           2 :   return rv;
     370             : }
     371             : 
     372             : void
     373           0 : nsUrlClassifierDBServiceWorker::ResetStream()
     374             : {
     375           0 :   LOG(("ResetStream"));
     376           0 :   mInStream = false;
     377           0 :   mProtocolParser = nullptr;
     378           0 : }
     379             : 
     380             : void
     381           1 : nsUrlClassifierDBServiceWorker::ResetUpdate()
     382             : {
     383           1 :   LOG(("ResetUpdate"));
     384           1 :   mUpdateWaitSec = 0;
     385           1 :   mUpdateStatus = NS_OK;
     386           1 :   mUpdateObserver = nullptr;
     387           1 : }
     388             : 
     389             : NS_IMETHODIMP
     390           0 : nsUrlClassifierDBServiceWorker::SetHashCompleter(const nsACString &tableName,
     391             :                                                  nsIUrlClassifierHashCompleter *completer)
     392             : {
     393           0 :   return NS_ERROR_NOT_IMPLEMENTED;
     394             : }
     395             : 
     396             : NS_IMETHODIMP
     397           2 : nsUrlClassifierDBServiceWorker::BeginUpdate(nsIUrlClassifierUpdateObserver *observer,
     398             :                                             const nsACString &tables)
     399             : {
     400           2 :   LOG(("nsUrlClassifierDBServiceWorker::BeginUpdate [%s]", PromiseFlatCString(tables).get()));
     401             : 
     402           2 :   if (gShuttingDownThread) {
     403           0 :     return NS_ERROR_NOT_INITIALIZED;
     404             :   }
     405             : 
     406           2 :   NS_ENSURE_STATE(!mUpdateObserver);
     407             : 
     408           2 :   nsresult rv = OpenDb();
     409           2 :   if (NS_FAILED(rv)) {
     410           0 :     NS_ERROR("Unable to open SafeBrowsing database");
     411           0 :     return NS_ERROR_FAILURE;
     412             :   }
     413             : 
     414           2 :   mUpdateStatus = NS_OK;
     415           2 :   mUpdateObserver = observer;
     416           2 :   Classifier::SplitTables(tables, mUpdateTables);
     417             : 
     418           2 :   return NS_OK;
     419             : }
     420             : 
     421             : // Called from the stream updater.
     422             : NS_IMETHODIMP
     423           1 : nsUrlClassifierDBServiceWorker::BeginStream(const nsACString &table)
     424             : {
     425           1 :   LOG(("nsUrlClassifierDBServiceWorker::BeginStream"));
     426           1 :   MOZ_ASSERT(!NS_IsMainThread(), "Streaming must be on the background thread");
     427             : 
     428           1 :   if (gShuttingDownThread) {
     429           0 :     return NS_ERROR_NOT_INITIALIZED;
     430             :   }
     431             : 
     432           1 :   NS_ENSURE_STATE(mUpdateObserver);
     433           1 :   NS_ENSURE_STATE(!mInStream);
     434             : 
     435           1 :   mInStream = true;
     436             : 
     437           1 :   NS_ASSERTION(!mProtocolParser, "Should not have a protocol parser.");
     438             : 
     439             :   // Check if we should use protobuf to parse the update.
     440           1 :   bool useProtobuf = false;
     441           7 :   for (size_t i = 0; i < mUpdateTables.Length(); i++) {
     442             :     bool isCurProtobuf =
     443           6 :       StringEndsWith(mUpdateTables[i], NS_LITERAL_CSTRING("-proto"));
     444             : 
     445           6 :     if (0 == i) {
     446             :       // Use the first table name to decice if all the subsequent tables
     447             :       // should be '-proto'.
     448           1 :       useProtobuf = isCurProtobuf;
     449           1 :       continue;
     450             :     }
     451             : 
     452           5 :     if (useProtobuf != isCurProtobuf) {
     453             :       NS_WARNING("Cannot mix 'proto' tables with other types "
     454           0 :                  "within the same provider.");
     455           0 :       break;
     456             :     }
     457             :   }
     458             : 
     459           0 :   mProtocolParser = (useProtobuf ? static_cast<ProtocolParser*>(new ProtocolParserProtobuf())
     460           2 :                                  : static_cast<ProtocolParser*>(new ProtocolParserV2()));
     461           1 :   if (!mProtocolParser)
     462           0 :     return NS_ERROR_OUT_OF_MEMORY;
     463             : 
     464           1 :   mProtocolParser->Init(mCryptoHash);
     465             : 
     466           1 :   if (!table.IsEmpty()) {
     467           0 :     mProtocolParser->SetCurrentTable(table);
     468             :   }
     469             : 
     470           1 :   mProtocolParser->SetRequestedTables(mUpdateTables);
     471             : 
     472           1 :   return NS_OK;
     473             : }
     474             : 
     475             : /**
     476             :  * Updating the database:
     477             :  *
     478             :  * The Update() method takes a series of chunks separated with control data,
     479             :  * as described in
     480             :  * http://code.google.com/p/google-safe-browsing/wiki/Protocolv2Spec
     481             :  *
     482             :  * It will iterate through the control data until it reaches a chunk.  By
     483             :  * the time it reaches a chunk, it should have received
     484             :  * a) the table to which this chunk applies
     485             :  * b) the type of chunk (add, delete, expire add, expire delete).
     486             :  * c) the chunk ID
     487             :  * d) the length of the chunk.
     488             :  *
     489             :  * For add and subtract chunks, it needs to read the chunk data (expires
     490             :  * don't have any data).  Chunk data is a list of URI fragments whose
     491             :  * encoding depends on the type of table (which is indicated by the end
     492             :  * of the table name):
     493             :  * a) tables ending with -exp are a zlib-compressed list of URI fragments
     494             :  *    separated by newlines.
     495             :  * b) tables ending with -sha128 have the form
     496             :  *    [domain][N][frag0]...[fragN]
     497             :  *       16    1   16        16
     498             :  *    If N is 0, the domain is reused as a fragment.
     499             :  * c) any other tables are assumed to be a plaintext list of URI fragments
     500             :  *    separated by newlines.
     501             :  *
     502             :  * Update() can be fed partial data;  It will accumulate data until there is
     503             :  * enough to act on.  Finish() should be called when there will be no more
     504             :  * data.
     505             :  */
     506             : NS_IMETHODIMP
     507           1 : nsUrlClassifierDBServiceWorker::UpdateStream(const nsACString& chunk)
     508             : {
     509           1 :   if (gShuttingDownThread) {
     510           0 :     return NS_ERROR_NOT_INITIALIZED;
     511             :   }
     512             : 
     513           1 :   NS_ENSURE_STATE(mInStream);
     514             : 
     515           1 :   HandlePendingLookups();
     516             : 
     517             :   // Feed the chunk to the parser.
     518           1 :   return mProtocolParser->AppendStream(chunk);
     519             : }
     520             : 
     521             : NS_IMETHODIMP
     522           1 : nsUrlClassifierDBServiceWorker::FinishStream()
     523             : {
     524           1 :   if (gShuttingDownThread) {
     525           0 :     LOG(("shutting down"));
     526           0 :     return NS_ERROR_NOT_INITIALIZED;
     527             :   }
     528             : 
     529           1 :   NS_ENSURE_STATE(mInStream);
     530           1 :   NS_ENSURE_STATE(mUpdateObserver);
     531             : 
     532           1 :   mInStream = false;
     533             : 
     534           1 :   mProtocolParser->End();
     535             : 
     536           1 :   if (NS_SUCCEEDED(mProtocolParser->Status())) {
     537           1 :     if (mProtocolParser->UpdateWaitSec()) {
     538           1 :       mUpdateWaitSec = mProtocolParser->UpdateWaitSec();
     539             :     }
     540             :     // XXX: Only allow forwards from the initial update?
     541             :     const nsTArray<ProtocolParser::ForwardedUpdate> &forwards =
     542           1 :       mProtocolParser->Forwards();
     543           1 :     for (uint32_t i = 0; i < forwards.Length(); i++) {
     544           0 :       const ProtocolParser::ForwardedUpdate &forward = forwards[i];
     545           0 :       mUpdateObserver->UpdateUrlRequested(forward.url, forward.table);
     546             :     }
     547             :     // Hold on to any TableUpdate objects that were created by the
     548             :     // parser.
     549           1 :     mTableUpdates.AppendElements(mProtocolParser->GetTableUpdates());
     550           1 :     mProtocolParser->ForgetTableUpdates();
     551             : 
     552             : #ifdef MOZ_SAFEBROWSING_DUMP_FAILED_UPDATES
     553             :     // The assignment involves no string copy since the source string is sharable.
     554           1 :     mRawTableUpdates = mProtocolParser->GetRawTableUpdates();
     555             : #endif
     556             :   } else {
     557           0 :     LOG(("nsUrlClassifierDBService::FinishStream Failed to parse the stream "
     558             :          "using mProtocolParser."));
     559           0 :     mUpdateStatus = mProtocolParser->Status();
     560             :   }
     561           1 :   mUpdateObserver->StreamFinished(mProtocolParser->Status(), 0);
     562             : 
     563           1 :   if (NS_SUCCEEDED(mUpdateStatus)) {
     564           1 :     if (mProtocolParser->ResetRequested()) {
     565           0 :       mClassifier->ResetTables(Classifier::Clear_All, mUpdateTables);
     566             :     }
     567             :   } else {
     568           0 :     mUpdateStatus = NS_ERROR_UC_UPDATE_PROTOCOL_PARSER_ERROR;
     569             :   }
     570             : 
     571           1 :   mProtocolParser = nullptr;
     572             : 
     573           1 :   return NS_OK;
     574             : }
     575             : 
     576             : NS_IMETHODIMP
     577           2 : nsUrlClassifierDBServiceWorker::FinishUpdate()
     578             : {
     579           2 :   LOG(("nsUrlClassifierDBServiceWorker::FinishUpdate"));
     580             : 
     581           2 :   MOZ_ASSERT(!NS_IsMainThread(), "nsUrlClassifierDBServiceWorker::FinishUpdate "
     582             :                                  "NUST NOT be on the main thread.");
     583             : 
     584           2 :   if (gShuttingDownThread) {
     585           0 :     return NS_ERROR_NOT_INITIALIZED;
     586             :   }
     587             : 
     588           2 :   MOZ_ASSERT(!mProtocolParser, "Should have been nulled out in FinishStream() "
     589             :                                "or never created.");
     590             : 
     591           2 :   NS_ENSURE_STATE(mUpdateObserver);
     592             : 
     593           2 :   if (NS_FAILED(mUpdateStatus)) {
     594           0 :     LOG(("nsUrlClassifierDBServiceWorker::FinishUpdate() Not running "
     595             :          "ApplyUpdate() since the update has already failed."));
     596           0 :     return NotifyUpdateObserver(mUpdateStatus);
     597             :   }
     598             : 
     599           2 :   if (mTableUpdates.IsEmpty()) {
     600           1 :     LOG(("Nothing to update. Just notify update observer."));
     601           1 :     return NotifyUpdateObserver(NS_OK);
     602             :   }
     603             : 
     604           2 :   RefPtr<nsUrlClassifierDBServiceWorker> self = this;
     605           2 :   nsresult rv = mClassifier->AsyncApplyUpdates(&mTableUpdates,
     606          12 :                                                [=] (nsresult aRv) -> void {
     607             : #ifdef MOZ_SAFEBROWSING_DUMP_FAILED_UPDATES
     608           2 :     if (NS_FAILED(aRv) &&
     609           1 :         NS_ERROR_OUT_OF_MEMORY != aRv &&
     610             :         NS_ERROR_UC_UPDATE_SHUTDOWNING != aRv) {
     611           0 :       self->mClassifier->DumpRawTableUpdates(mRawTableUpdates);
     612             :     }
     613             :     // Invalidate the raw table updates.
     614           1 :     self->mRawTableUpdates = EmptyCString();
     615             : #endif
     616             : 
     617           1 :     self->NotifyUpdateObserver(aRv);
     618           2 :   });
     619             : 
     620           1 :   if (NS_FAILED(rv)) {
     621           0 :     LOG(("Failed to start async update. Notify immediately."));
     622           0 :     NotifyUpdateObserver(rv);
     623             :   }
     624             : 
     625           1 :   return rv;
     626             : }
     627             : 
     628             : nsresult
     629           2 : nsUrlClassifierDBServiceWorker::NotifyUpdateObserver(nsresult aUpdateStatus)
     630             : {
     631           2 :   MOZ_ASSERT(!NS_IsMainThread(), "nsUrlClassifierDBServiceWorker::NotifyUpdateObserver "
     632             :                                  "NUST NOT be on the main thread.");
     633             : 
     634           2 :   LOG(("nsUrlClassifierDBServiceWorker::NotifyUpdateObserver"));
     635             : 
     636             :   // We've either
     637             :   //  1) failed starting a download stream
     638             :   //  2) succeeded in starting a download stream but failed to obtain
     639             :   //     table updates
     640             :   //  3) succeeded in obtaining table updates but failed to build new
     641             :   //     tables.
     642             :   //  4) succeeded in building new tables but failed to take them.
     643             :   //  5) succeeded in taking new tables.
     644             : 
     645           2 :   mUpdateStatus = aUpdateStatus;
     646             : 
     647             :   nsCOMPtr<nsIUrlClassifierUtils> urlUtil =
     648           4 :     do_GetService(NS_URLCLASSIFIERUTILS_CONTRACTID);
     649             : 
     650           4 :   nsCString provider;
     651             :   // Assume that all the tables in update should have the same provider.
     652           2 :   urlUtil->GetTelemetryProvider(mUpdateTables.SafeElementAt(0, EmptyCString()), provider);
     653             : 
     654           2 :   nsresult updateStatus = mUpdateStatus;
     655           2 :   if (NS_FAILED(mUpdateStatus)) {
     656           0 :    updateStatus = NS_ERROR_GET_MODULE(mUpdateStatus) == NS_ERROR_MODULE_URL_CLASSIFIER ?
     657             :      mUpdateStatus : NS_ERROR_UC_UPDATE_UNKNOWN;
     658             :   }
     659             : 
     660             :   // Do not record telemetry for testing tables.
     661           2 :   if (!provider.Equals(TESTING_TABLE_PROVIDER_NAME)) {
     662           1 :     Telemetry::Accumulate(Telemetry::URLCLASSIFIER_UPDATE_ERROR, provider,
     663           2 :                           NS_ERROR_GET_CODE(updateStatus));
     664             :   }
     665             : 
     666           2 :   if (!mUpdateObserver) {
     667             :     // In the normal shutdown process, CancelUpdate() would NOT be
     668             :     // called prior to NotifyUpdateObserver(). However, CancelUpdate()
     669             :     // is a public API which can be called in the test case at any point.
     670             :     // If the call sequence is FinishUpdate() then CancelUpdate(), the later
     671             :     // might be executed before NotifyUpdateObserver() which is triggered
     672             :     // by the update thread. In this case, we will get null mUpdateObserver.
     673             :     NS_WARNING("CancelUpdate() is called before we asynchronously call "
     674           0 :                "NotifyUpdateObserver() in FinishUpdate().");
     675             : 
     676             :     // The DB cleanup will be done in CancelUpdate() so we can just return.
     677           0 :     return NS_OK;
     678             :   }
     679             : 
     680             :   // Null out mUpdateObserver before notifying so that BeginUpdate()
     681             :   // becomes available prior to callback.
     682           4 :   nsCOMPtr<nsIUrlClassifierUpdateObserver> updateObserver = nullptr;
     683           2 :   updateObserver.swap(mUpdateObserver);
     684             : 
     685           2 :   if (NS_SUCCEEDED(mUpdateStatus)) {
     686           2 :     LOG(("Notifying success: %d", mUpdateWaitSec));
     687           2 :     updateObserver->UpdateSuccess(mUpdateWaitSec);
     688           0 :   } else if (NS_ERROR_NOT_IMPLEMENTED == mUpdateStatus) {
     689           0 :     LOG(("Treating NS_ERROR_NOT_IMPLEMENTED a successful update "
     690             :          "but still mark it spoiled."));
     691           0 :     updateObserver->UpdateSuccess(0);
     692           0 :     mClassifier->ResetTables(Classifier::Clear_Cache, mUpdateTables);
     693             :   } else {
     694           0 :     if (LOG_ENABLED()) {
     695           0 :       nsAutoCString errorName;
     696           0 :       mozilla::GetErrorName(mUpdateStatus, errorName);
     697           0 :       LOG(("Notifying error: %s (%" PRIu32 ")", errorName.get(),
     698             :            static_cast<uint32_t>(mUpdateStatus)));
     699             :     }
     700             : 
     701           0 :     updateObserver->UpdateError(mUpdateStatus);
     702             :     /*
     703             :      * mark the tables as spoiled(clear cache in LookupCache), we don't want to
     704             :      * block hosts longer than normal because our update failed
     705             :     */
     706           0 :     mClassifier->ResetTables(Classifier::Clear_Cache, mUpdateTables);
     707             :   }
     708             : 
     709           2 :   return NS_OK;
     710             : }
     711             : 
     712             : NS_IMETHODIMP
     713           0 : nsUrlClassifierDBServiceWorker::ResetDatabase()
     714             : {
     715           0 :   nsresult rv = OpenDb();
     716             : 
     717           0 :   if (NS_SUCCEEDED(rv)) {
     718           0 :     mClassifier->Reset();
     719             :   }
     720             : 
     721           0 :   rv = CloseDb();
     722           0 :   NS_ENSURE_SUCCESS(rv, rv);
     723             : 
     724           0 :   return NS_OK;
     725             : }
     726             : 
     727             : NS_IMETHODIMP
     728           0 : nsUrlClassifierDBServiceWorker::ReloadDatabase()
     729             : {
     730           0 :   nsTArray<nsCString> tables;
     731           0 :   nsresult rv = mClassifier->ActiveTables(tables);
     732           0 :   NS_ENSURE_SUCCESS(rv, rv);
     733             : 
     734             :   // This will null out mClassifier
     735           0 :   rv = CloseDb();
     736           0 :   NS_ENSURE_SUCCESS(rv, rv);
     737             : 
     738             :   // Create new mClassifier and load prefixset and completions from disk.
     739           0 :   rv = OpenDb();
     740           0 :   NS_ENSURE_SUCCESS(rv, rv);
     741             : 
     742           0 :   return NS_OK;
     743             : }
     744             : 
     745             : NS_IMETHODIMP
     746           0 : nsUrlClassifierDBServiceWorker::ClearCache()
     747             : {
     748           0 :   nsTArray<nsCString> tables;
     749           0 :   nsresult rv = mClassifier->ActiveTables(tables);
     750           0 :   NS_ENSURE_SUCCESS(rv, rv);
     751             : 
     752           0 :   mClassifier->ResetTables(Classifier::Clear_Cache, tables);
     753             : 
     754           0 :   return NS_OK;
     755             : }
     756             : 
     757             : NS_IMETHODIMP
     758           0 : nsUrlClassifierDBServiceWorker::CancelUpdate()
     759             : {
     760           0 :   LOG(("nsUrlClassifierDBServiceWorker::CancelUpdate"));
     761             : 
     762           0 :   if (mUpdateObserver) {
     763           0 :     LOG(("UpdateObserver exists, cancelling"));
     764             : 
     765           0 :     mUpdateStatus = NS_BINDING_ABORTED;
     766             : 
     767           0 :     mUpdateObserver->UpdateError(mUpdateStatus);
     768             : 
     769             :     /*
     770             :      * mark the tables as spoiled(clear cache in LookupCache), we don't want to
     771             :      * block hosts longer than normal because our update failed
     772             :     */
     773           0 :     mClassifier->ResetTables(Classifier::Clear_Cache, mUpdateTables);
     774             : 
     775           0 :     ResetStream();
     776           0 :     ResetUpdate();
     777             :   } else {
     778           0 :     LOG(("No UpdateObserver, nothing to cancel"));
     779             :   }
     780             : 
     781           0 :   return NS_OK;
     782             : }
     783             : 
     784             : void
     785           0 : nsUrlClassifierDBServiceWorker::FlushAndDisableAsyncUpdate()
     786             : {
     787           0 :   LOG(("nsUrlClassifierDBServiceWorker::FlushAndDisableAsyncUpdate()"));
     788             : 
     789           0 :   if (mClassifier) {
     790           0 :     mClassifier->FlushAndDisableAsyncUpdate();
     791             :   }
     792           0 : }
     793             : 
     794             : // Allows the main thread to delete the connection which may be in
     795             : // a background thread.
     796             : // XXX This could be turned into a single shutdown event so the logic
     797             : // is simpler in nsUrlClassifierDBService::Shutdown.
     798             : nsresult
     799           0 : nsUrlClassifierDBServiceWorker::CloseDb()
     800             : {
     801           0 :   if (mClassifier) {
     802           0 :     mClassifier->Close();
     803           0 :     mClassifier = nullptr;
     804             :   }
     805             : 
     806           0 :   mCryptoHash = nullptr;
     807           0 :   LOG(("urlclassifier db closed\n"));
     808             : 
     809           0 :   return NS_OK;
     810             : }
     811             : 
     812             : nsresult
     813           0 : nsUrlClassifierDBServiceWorker::CacheCompletions(CacheResultArray *results)
     814             : {
     815           0 :   if (gShuttingDownThread) {
     816           0 :     return NS_ERROR_ABORT;
     817             :   }
     818             : 
     819           0 :   LOG(("nsUrlClassifierDBServiceWorker::CacheCompletions [%p]", this));
     820           0 :   if (!mClassifier) {
     821           0 :     return NS_OK;
     822             :   }
     823             : 
     824             :   // Ownership is transferred in to us
     825           0 :   nsAutoPtr<CacheResultArray> resultsPtr(results);
     826             : 
     827           0 :   if (resultsPtr->Length() == 0) {
     828           0 :     return NS_OK;
     829             :   }
     830             : 
     831           0 :   if (IsSameAsLastResults(*resultsPtr)) {
     832           0 :     LOG(("Skipping completions that have just been cached already."));
     833           0 :     return NS_OK;
     834             :   }
     835             : 
     836             :   // Only cache results for tables that we have, don't take
     837             :   // in tables we might accidentally have hit during a completion.
     838             :   // This happens due to goog vs googpub lists existing.
     839           0 :   nsTArray<nsCString> tables;
     840           0 :   nsresult rv = mClassifier->ActiveTables(tables);
     841           0 :   NS_ENSURE_SUCCESS(rv, rv);
     842             : 
     843           0 :   nsTArray<TableUpdate*> updates;
     844             : 
     845           0 :   for (uint32_t i = 0; i < resultsPtr->Length(); i++) {
     846           0 :     bool activeTable = false;
     847           0 :     CacheResult* result = resultsPtr->ElementAt(i).get();
     848             : 
     849           0 :     for (uint32_t table = 0; table < tables.Length(); table++) {
     850           0 :       if (tables[table].Equals(result->table)) {
     851           0 :         activeTable = true;
     852           0 :         break;
     853             :       }
     854             :     }
     855           0 :     if (activeTable) {
     856           0 :       nsAutoPtr<ProtocolParser> pParse;
     857           0 :       pParse = result->Ver() == CacheResult::V2 ?
     858           0 :                  static_cast<ProtocolParser*>(new ProtocolParserV2()) :
     859           0 :                  static_cast<ProtocolParser*>(new ProtocolParserProtobuf());
     860             : 
     861           0 :       TableUpdate* tu = pParse->GetTableUpdate(result->table);
     862             : 
     863           0 :       rv = CacheResultToTableUpdate(result, tu);
     864           0 :       if (NS_FAILED(rv)) {
     865             :         // We can bail without leaking here because ForgetTableUpdates
     866             :         // hasn't been called yet.
     867           0 :         return rv;
     868             :       }
     869           0 :       updates.AppendElement(tu);
     870           0 :       pParse->ForgetTableUpdates();
     871             :     } else {
     872           0 :       LOG(("Completion received, but table is not active, so not caching."));
     873             :     }
     874             :    }
     875             : 
     876           0 :   mClassifier->ApplyFullHashes(&updates);
     877           0 :   mLastResults = Move(resultsPtr);
     878           0 :   return NS_OK;
     879             : }
     880             : 
     881             : nsresult
     882           0 : nsUrlClassifierDBServiceWorker::CacheResultToTableUpdate(CacheResult* aCacheResult,
     883             :                                                          TableUpdate* aUpdate)
     884             : {
     885           0 :   auto tuV2 = TableUpdate::Cast<TableUpdateV2>(aUpdate);
     886           0 :   if (tuV2) {
     887           0 :     auto result = CacheResult::Cast<CacheResultV2>(aCacheResult);
     888           0 :     MOZ_ASSERT(result);
     889             : 
     890           0 :     if (result->miss) {
     891           0 :       return tuV2->NewMissPrefix(result->prefix);
     892             :     } else {
     893           0 :       LOG(("CacheCompletion hash %X, Addchunk %d", result->completion.ToUint32(),
     894             :            result->addChunk));
     895             : 
     896           0 :       nsresult rv = tuV2->NewAddComplete(result->addChunk, result->completion);
     897           0 :       if (NS_FAILED(rv)) {
     898           0 :         return rv;
     899             :       }
     900           0 :       return tuV2->NewAddChunk(result->addChunk);
     901             :     }
     902             :   }
     903             : 
     904           0 :   auto tuV4 = TableUpdate::Cast<TableUpdateV4>(aUpdate);
     905           0 :   if (tuV4) {
     906           0 :     auto result = CacheResult::Cast<CacheResultV4>(aCacheResult);
     907           0 :     MOZ_ASSERT(result);
     908             : 
     909           0 :     if (LOG_ENABLED()) {
     910           0 :       const FullHashExpiryCache& fullHashes = result->response.fullHashes;
     911           0 :       for (auto iter = fullHashes.ConstIter(); !iter.Done(); iter.Next()) {
     912             :         Completion completion;
     913           0 :         completion.Assign(iter.Key());
     914           0 :         LOG(("CacheCompletion(v4) hash %X, CacheExpireTime %" PRId64,
     915             :              completion.ToUint32(), iter.Data()));
     916             :       }
     917             :     }
     918             : 
     919           0 :     tuV4->NewFullHashResponse(result->prefix, result->response);
     920           0 :     return NS_OK;
     921             :   }
     922             : 
     923             :   // tableUpdate object should be either V2 or V4.
     924           0 :   return NS_ERROR_FAILURE;
     925             : }
     926             : 
     927             : nsresult
     928           5 : nsUrlClassifierDBServiceWorker::OpenDb()
     929             : {
     930           5 :   if (gShuttingDownThread) {
     931           0 :     return NS_ERROR_ABORT;
     932             :   }
     933             : 
     934           5 :   MOZ_ASSERT(!NS_IsMainThread(), "Must initialize DB on background thread");
     935             :   // Connection already open, don't do anything.
     936           5 :   if (mClassifier) {
     937           4 :     return NS_OK;
     938             :   }
     939             : 
     940             :   nsresult rv;
     941           1 :   mCryptoHash = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
     942           1 :   NS_ENSURE_SUCCESS(rv, rv);
     943             : 
     944           2 :   nsAutoPtr<Classifier> classifier(new Classifier());
     945           1 :   if (!classifier) {
     946           0 :     return NS_ERROR_OUT_OF_MEMORY;
     947             :   }
     948             : 
     949           1 :   rv = classifier->Open(*mCacheDir);
     950           1 :   NS_ENSURE_SUCCESS(rv, rv);
     951             : 
     952           1 :   mClassifier = classifier;
     953             : 
     954           1 :   return NS_OK;
     955             : }
     956             : 
     957             : NS_IMETHODIMP
     958           0 : nsUrlClassifierDBServiceWorker::ClearLastResults()
     959             : {
     960           0 :   MOZ_ASSERT(!NS_IsMainThread(), "Must be on the background thread");
     961           0 :   if (mLastResults) {
     962           0 :     mLastResults->Clear();
     963             :   }
     964           0 :   return NS_OK;
     965             : }
     966             : 
     967             : nsresult
     968           0 : nsUrlClassifierDBServiceWorker::GetCacheInfo(const nsACString& aTable,
     969             :                                              nsIUrlClassifierCacheInfo** aCache)
     970             : {
     971           0 :   MOZ_ASSERT(!NS_IsMainThread(), "Must be on the background thread");
     972           0 :   if (!mClassifier) {
     973           0 :     return NS_ERROR_NOT_AVAILABLE;
     974             :   }
     975             : 
     976           0 :   mClassifier->GetCacheInfo(aTable, aCache);
     977           0 :   return NS_OK;
     978             : }
     979             : 
     980             : bool
     981           0 : nsUrlClassifierDBServiceWorker::IsSameAsLastResults(CacheResultArray& aResult)
     982             : {
     983           0 :   if (!mLastResults || mLastResults->Length() != aResult.Length()) {
     984           0 :     return false;
     985             :   }
     986             : 
     987           0 :   bool equal = true;
     988           0 :   for (uint32_t i = 0; i < mLastResults->Length() && equal; i++) {
     989           0 :     CacheResult* lhs = mLastResults->ElementAt(i).get();
     990           0 :     CacheResult* rhs = aResult[i].get();
     991             : 
     992           0 :     if (lhs->Ver() != rhs->Ver()) {
     993           0 :       return false;
     994             :     }
     995             : 
     996           0 :     if (lhs->Ver() == CacheResult::V2) {
     997           0 :       equal = *(CacheResult::Cast<CacheResultV2>(lhs)) ==
     998           0 :               *(CacheResult::Cast<CacheResultV2>(rhs));
     999           0 :     } else if (lhs->Ver() == CacheResult::V4) {
    1000           0 :       equal = *(CacheResult::Cast<CacheResultV4>(lhs)) ==
    1001           0 :               *(CacheResult::Cast<CacheResultV4>(rhs));
    1002             :     }
    1003             :   }
    1004             : 
    1005           0 :   return equal;
    1006             : }
    1007             : 
    1008             : // -------------------------------------------------------------------------
    1009             : // nsUrlClassifierLookupCallback
    1010             : //
    1011             : // This class takes the results of a lookup found on the worker thread
    1012             : // and handles any necessary partial hash expansions before calling
    1013             : // the client callback.
    1014             : 
    1015             : class nsUrlClassifierLookupCallback final : public nsIUrlClassifierLookupCallback
    1016             :                                           , public nsIUrlClassifierHashCompleterCallback
    1017             : {
    1018             : public:
    1019             :   NS_DECL_THREADSAFE_ISUPPORTS
    1020             :   NS_DECL_NSIURLCLASSIFIERLOOKUPCALLBACK
    1021             :   NS_DECL_NSIURLCLASSIFIERHASHCOMPLETERCALLBACK
    1022             : 
    1023           0 :   nsUrlClassifierLookupCallback(nsUrlClassifierDBService *dbservice,
    1024             :                                 nsIUrlClassifierCallback *c)
    1025           0 :     : mDBService(dbservice)
    1026             :     , mResults(nullptr)
    1027             :     , mPendingCompletions(0)
    1028           0 :     , mCallback(c)
    1029           0 :     {}
    1030             : 
    1031             : private:
    1032             :   ~nsUrlClassifierLookupCallback();
    1033             : 
    1034             :   nsresult HandleResults();
    1035             :   nsresult ProcessComplete(CacheResult* aCacheResult);
    1036             :   nsresult CacheMisses();
    1037             : 
    1038             :   RefPtr<nsUrlClassifierDBService> mDBService;
    1039             :   nsAutoPtr<LookupResultArray> mResults;
    1040             : 
    1041             :   // Completed results to send back to the worker for caching.
    1042             :   nsAutoPtr<CacheResultArray> mCacheResults;
    1043             : 
    1044             :   uint32_t mPendingCompletions;
    1045             :   nsCOMPtr<nsIUrlClassifierCallback> mCallback;
    1046             : };
    1047             : 
    1048           0 : NS_IMPL_ISUPPORTS(nsUrlClassifierLookupCallback,
    1049             :                   nsIUrlClassifierLookupCallback,
    1050             :                   nsIUrlClassifierHashCompleterCallback)
    1051             : 
    1052           0 : nsUrlClassifierLookupCallback::~nsUrlClassifierLookupCallback()
    1053             : {
    1054           0 :   if (mCallback) {
    1055             :     NS_ReleaseOnMainThread(
    1056           0 :       "nsUrlClassifierLookupCallback::mCallback", mCallback.forget());
    1057             :   }
    1058           0 : }
    1059             : 
    1060             : NS_IMETHODIMP
    1061           0 : nsUrlClassifierLookupCallback::LookupComplete(nsTArray<LookupResult>* results)
    1062             : {
    1063           0 :   NS_ASSERTION(mResults == nullptr,
    1064             :                "Should only get one set of results per nsUrlClassifierLookupCallback!");
    1065             : 
    1066           0 :   if (!results) {
    1067           0 :     HandleResults();
    1068           0 :     return NS_OK;
    1069             :   }
    1070             : 
    1071           0 :   mResults = results;
    1072             : 
    1073             :   // Check the results entries that need to be completed.
    1074           0 :   for (uint32_t i = 0; i < results->Length(); i++) {
    1075           0 :     LookupResult& result = results->ElementAt(i);
    1076             : 
    1077             :     // We will complete partial matches and matches that are stale.
    1078           0 :     if (!result.Confirmed()) {
    1079           0 :       nsCOMPtr<nsIUrlClassifierHashCompleter> completer;
    1080           0 :       nsCString gethashUrl;
    1081             :       nsresult rv;
    1082           0 :       nsCOMPtr<nsIUrlListManager> listManager = do_GetService(
    1083           0 :         "@mozilla.org/url-classifier/listmanager;1", &rv);
    1084           0 :       NS_ENSURE_SUCCESS(rv, rv);
    1085           0 :       rv = listManager->GetGethashUrl(result.mTableName, gethashUrl);
    1086           0 :       NS_ENSURE_SUCCESS(rv, rv);
    1087           0 :       LOG(("The match from %s needs to be completed at %s",
    1088             :            result.mTableName.get(), gethashUrl.get()));
    1089             :       // gethashUrls may be empty in 2 cases: test tables, and on startup where
    1090             :       // we may have found a prefix in an existing table before the listmanager
    1091             :       // has registered the table. In the second case we should not call
    1092             :       // complete.
    1093           0 :       if ((!gethashUrl.IsEmpty() ||
    1094           0 :            StringBeginsWith(result.mTableName, NS_LITERAL_CSTRING("test"))) &&
    1095           0 :           mDBService->GetCompleter(result.mTableName,
    1096           0 :                                    getter_AddRefs(completer))) {
    1097             : 
    1098             :         // Bug 1323953 - Send the first 4 bytes for completion no matter how
    1099             :         // long we matched the prefix.
    1100           0 :         nsresult rv = completer->Complete(result.PartialHash(),
    1101             :                                           gethashUrl,
    1102             :                                           result.mTableName,
    1103           0 :                                           this);
    1104           0 :         if (NS_SUCCEEDED(rv)) {
    1105           0 :           mPendingCompletions++;
    1106             :         }
    1107             :       } else {
    1108             :         // For tables with no hash completer, a complete hash match is
    1109             :         // good enough, we'll consider it is valid.
    1110           0 :         if (result.Complete()) {
    1111           0 :           result.mConfirmed = true;
    1112           0 :           LOG(("Skipping completion in a table without a valid completer (%s).",
    1113             :                result.mTableName.get()));
    1114             :         } else {
    1115           0 :           NS_WARNING("Partial match in a table without a valid completer, ignoring partial match.");
    1116             :         }
    1117             :       }
    1118             :     }
    1119             :   }
    1120             : 
    1121           0 :   LOG(("nsUrlClassifierLookupCallback::LookupComplete [%p] "
    1122             :        "%u pending completions", this, mPendingCompletions));
    1123           0 :   if (mPendingCompletions == 0) {
    1124             :     // All results were complete, we're ready!
    1125           0 :     HandleResults();
    1126             :   }
    1127             : 
    1128           0 :   return NS_OK;
    1129             : }
    1130             : 
    1131             : NS_IMETHODIMP
    1132           0 : nsUrlClassifierLookupCallback::CompletionFinished(nsresult status)
    1133             : {
    1134           0 :   if (LOG_ENABLED()) {
    1135           0 :     nsAutoCString errorName;
    1136           0 :     mozilla::GetErrorName(status, errorName);
    1137           0 :     LOG(("nsUrlClassifierLookupCallback::CompletionFinished [%p, %s]",
    1138             :          this, errorName.get()));
    1139             :   }
    1140             : 
    1141           0 :   mPendingCompletions--;
    1142           0 :   if (mPendingCompletions == 0) {
    1143           0 :     HandleResults();
    1144             :   }
    1145             : 
    1146           0 :   return NS_OK;
    1147             : }
    1148             : 
    1149             : NS_IMETHODIMP
    1150           0 : nsUrlClassifierLookupCallback::CompletionV2(const nsACString& aCompleteHash,
    1151             :                                             const nsACString& aTableName,
    1152             :                                             uint32_t aChunkId)
    1153             : {
    1154           0 :   LOG(("nsUrlClassifierLookupCallback::Completion [%p, %s, %d]",
    1155             :        this, PromiseFlatCString(aTableName).get(), aChunkId));
    1156             : 
    1157           0 :   MOZ_ASSERT(!StringEndsWith(aTableName, NS_LITERAL_CSTRING("-proto")));
    1158             : 
    1159           0 :   nsAutoPtr<CacheResultV2> result(new CacheResultV2);
    1160             : 
    1161           0 :   result->table = aTableName;
    1162           0 :   result->prefix.Assign(aCompleteHash);
    1163           0 :   result->completion.Assign(aCompleteHash);
    1164           0 :   result->addChunk = aChunkId;
    1165             : 
    1166           0 :   return ProcessComplete(result.forget());
    1167             : }
    1168             : 
    1169             : NS_IMETHODIMP
    1170           0 : nsUrlClassifierLookupCallback::CompletionV4(const nsACString& aPartialHash,
    1171             :                                             const nsACString& aTableName,
    1172             :                                             uint32_t aNegativeCacheDuration,
    1173             :                                             nsIArray* aFullHashes)
    1174             : {
    1175           0 :   LOG(("nsUrlClassifierLookupCallback::CompletionV4 [%p, %s, %d]",
    1176             :        this, PromiseFlatCString(aTableName).get(), aNegativeCacheDuration));
    1177             : 
    1178           0 :   MOZ_ASSERT(StringEndsWith(aTableName, NS_LITERAL_CSTRING("-proto")));
    1179             : 
    1180           0 :   if(!aFullHashes) {
    1181           0 :     return NS_ERROR_INVALID_ARG;
    1182             :   }
    1183             : 
    1184           0 :   if (aNegativeCacheDuration > MAXIMUM_NEGATIVE_CACHE_DURATION_SEC) {
    1185           0 :     LOG(("Negative cache duration too large, clamping it down to"
    1186             :          "a reasonable value."));
    1187           0 :     aNegativeCacheDuration = MAXIMUM_NEGATIVE_CACHE_DURATION_SEC;
    1188             :   }
    1189             : 
    1190           0 :   nsAutoPtr<CacheResultV4> result(new CacheResultV4);
    1191             : 
    1192           0 :   int64_t nowSec = PR_Now() / PR_USEC_PER_SEC;
    1193             : 
    1194           0 :   result->table = aTableName;
    1195           0 :   result->prefix.Assign(aPartialHash);
    1196           0 :   result->response.negativeCacheExpirySec = nowSec + aNegativeCacheDuration;
    1197             : 
    1198             :   // Fill in positive cache entries.
    1199           0 :   uint32_t fullHashCount = 0;
    1200           0 :   nsresult rv = aFullHashes->GetLength(&fullHashCount);
    1201           0 :   if (NS_FAILED(rv)) {
    1202           0 :     return rv;
    1203             :   }
    1204             : 
    1205           0 :   for (uint32_t i = 0; i < fullHashCount; i++) {
    1206           0 :     nsCOMPtr<nsIFullHashMatch> match = do_QueryElementAt(aFullHashes, i);
    1207             : 
    1208           0 :     nsCString fullHash;
    1209           0 :     match->GetFullHash(fullHash);
    1210             : 
    1211             :     uint32_t duration;
    1212           0 :     match->GetCacheDuration(&duration);
    1213             : 
    1214           0 :     result->response.fullHashes.Put(fullHash, nowSec + duration);
    1215             :   }
    1216             : 
    1217           0 :   return ProcessComplete(result.forget());
    1218             : }
    1219             : 
    1220             : nsresult
    1221           0 : nsUrlClassifierLookupCallback::ProcessComplete(CacheResult* aCacheResult)
    1222             : {
    1223             :   // Send this completion to the store for caching.
    1224           0 :   if (!mCacheResults) {
    1225           0 :     mCacheResults = new CacheResultArray();
    1226           0 :     if (!mCacheResults) {
    1227           0 :       return NS_ERROR_OUT_OF_MEMORY;
    1228             :     }
    1229             :   }
    1230             : 
    1231             :   // OK if this fails, we just won't cache the item.
    1232           0 :   mCacheResults->AppendElement(aCacheResult);
    1233             : 
    1234             :   // Check if this matched any of our results.
    1235           0 :   for (uint32_t i = 0; i < mResults->Length(); i++) {
    1236           0 :     LookupResult& result = mResults->ElementAt(i);
    1237             : 
    1238             :     // Now, see if it verifies a lookup
    1239           0 :     if (!result.mNoise
    1240           0 :         && result.mTableName.Equals(aCacheResult->table)
    1241           0 :         && aCacheResult->findCompletion(result.CompleteHash())) {
    1242           0 :       result.mProtocolConfirmed = true;
    1243             :     }
    1244             :   }
    1245             : 
    1246           0 :   return NS_OK;
    1247             : }
    1248             : 
    1249             : nsresult
    1250           0 : nsUrlClassifierLookupCallback::HandleResults()
    1251             : {
    1252           0 :   if (!mResults) {
    1253             :     // No results, this URI is clean.
    1254           0 :     LOG(("nsUrlClassifierLookupCallback::HandleResults [%p, no results]", this));
    1255           0 :     return mCallback->HandleEvent(NS_LITERAL_CSTRING(""));
    1256             :   }
    1257           0 :   MOZ_ASSERT(mPendingCompletions == 0, "HandleResults() should never be "
    1258             :              "called while there are pending completions");
    1259             : 
    1260           0 :   LOG(("nsUrlClassifierLookupCallback::HandleResults [%p, %" PRIuSIZE " results]",
    1261             :        this, mResults->Length()));
    1262             : 
    1263             :   nsCOMPtr<nsIUrlClassifierClassifyCallback> classifyCallback =
    1264           0 :     do_QueryInterface(mCallback);
    1265             : 
    1266           0 :   nsTArray<nsCString> tables;
    1267             :   // Build a stringified list of result tables.
    1268           0 :   for (uint32_t i = 0; i < mResults->Length(); i++) {
    1269           0 :     LookupResult& result = mResults->ElementAt(i);
    1270             : 
    1271             :     // Leave out results that weren't confirmed, as their existence on
    1272             :     // the list can't be verified.  Also leave out randomly-generated
    1273             :     // noise.
    1274           0 :     if (result.mNoise) {
    1275           0 :       LOG(("Skipping result %s from table %s (noise)",
    1276             :            result.PartialHashHex().get(), result.mTableName.get()));
    1277           0 :       continue;
    1278             :     }
    1279             : 
    1280           0 :     if (!result.Confirmed()) {
    1281           0 :       LOG(("Skipping result %s from table %s (not confirmed)",
    1282             :            result.PartialHashHex().get(), result.mTableName.get()));
    1283           0 :       continue;
    1284             :     }
    1285             : 
    1286           0 :     LOG(("Confirmed result %s from table %s",
    1287             :          result.PartialHashHex().get(), result.mTableName.get()));
    1288             : 
    1289           0 :     if (tables.IndexOf(result.mTableName) == nsTArray<nsCString>::NoIndex) {
    1290           0 :       tables.AppendElement(result.mTableName);
    1291             :     }
    1292             : 
    1293           0 :     if (classifyCallback) {
    1294           0 :       nsCString prefixString;
    1295           0 :       result.hash.fixedLengthPrefix.ToString(prefixString);
    1296           0 :       classifyCallback->HandleResult(result.mTableName, prefixString);
    1297             :     }
    1298             :   }
    1299             : 
    1300             :   // Some parts of this gethash request generated no hits at all.
    1301             :   // Save the prefixes we checked to prevent repeated requests.
    1302           0 :   CacheMisses();
    1303             : 
    1304           0 :   if (mCacheResults) {
    1305             :     // This hands ownership of the cache results array back to the worker
    1306             :     // thread.
    1307           0 :     mDBService->CacheCompletions(mCacheResults.forget());
    1308             :   }
    1309             : 
    1310           0 :   nsAutoCString tableStr;
    1311           0 :   for (uint32_t i = 0; i < tables.Length(); i++) {
    1312           0 :     if (i != 0)
    1313           0 :       tableStr.Append(',');
    1314           0 :     tableStr.Append(tables[i]);
    1315             :   }
    1316             : 
    1317           0 :   return mCallback->HandleEvent(tableStr);
    1318             : }
    1319             : 
    1320             : nsresult
    1321           0 : nsUrlClassifierLookupCallback::CacheMisses()
    1322             : {
    1323           0 :   for (uint32_t i = 0; i < mResults->Length(); i++) {
    1324           0 :     const LookupResult &result = mResults->ElementAt(i);
    1325             :     // Skip V4 because cache information is already included in the
    1326             :     // fullhash response so we don't need to manually add it here.
    1327           0 :     if (!result.mProtocolV2 || result.Confirmed() || result.mNoise) {
    1328           0 :       continue;
    1329             :     }
    1330             : 
    1331           0 :     if (!mCacheResults) {
    1332           0 :       mCacheResults = new CacheResultArray();
    1333           0 :       if (!mCacheResults) {
    1334           0 :         return NS_ERROR_OUT_OF_MEMORY;
    1335             :       }
    1336             :     }
    1337             : 
    1338           0 :     auto cacheResult = new CacheResultV2;
    1339             : 
    1340           0 :     cacheResult->table = result.mTableName;
    1341           0 :     cacheResult->prefix = result.hash.fixedLengthPrefix;
    1342           0 :     cacheResult->miss = true;
    1343           0 :     mCacheResults->AppendElement(cacheResult);
    1344             :   }
    1345           0 :   return NS_OK;
    1346             : }
    1347             : 
    1348           0 : struct Provider {
    1349             :   nsCString name;
    1350             :   uint8_t priority;
    1351             : };
    1352             : 
    1353             : // Order matters
    1354             : // Provider which is not included in this table has the lowest priority 0
    1355           3 : static const Provider kBuiltInProviders[] = {
    1356           6 :   { NS_LITERAL_CSTRING("mozilla"), 1 },
    1357           6 :   { NS_LITERAL_CSTRING("google4"), 2 },
    1358           6 :   { NS_LITERAL_CSTRING("google"), 3 },
    1359          21 : };
    1360             : 
    1361             : // -------------------------------------------------------------------------
    1362             : // Helper class for nsIURIClassifier implementation, handle classify result and
    1363             : // send back to nsIURIClassifier
    1364             : 
    1365             : class nsUrlClassifierClassifyCallback final : public nsIUrlClassifierCallback,
    1366             :                                               public nsIUrlClassifierClassifyCallback
    1367             : {
    1368             : public:
    1369             :   NS_DECL_THREADSAFE_ISUPPORTS
    1370             :   NS_DECL_NSIURLCLASSIFIERCALLBACK
    1371             :   NS_DECL_NSIURLCLASSIFIERCLASSIFYCALLBACK
    1372             : 
    1373           0 :   explicit nsUrlClassifierClassifyCallback(nsIURIClassifierCallback *c)
    1374           0 :     : mCallback(c)
    1375           0 :     {}
    1376             : 
    1377             : private:
    1378             : 
    1379           0 :   struct ClassifyMatchedInfo {
    1380             :     nsCString table;
    1381             :     nsCString prefix;
    1382             :     Provider provider;
    1383             :     nsresult errorCode;
    1384             :   };
    1385             : 
    1386           0 :   ~nsUrlClassifierClassifyCallback() {};
    1387             : 
    1388             :   nsCOMPtr<nsIURIClassifierCallback> mCallback;
    1389             :   nsTArray<ClassifyMatchedInfo> mMatchedArray;
    1390             : };
    1391             : 
    1392           0 : NS_IMPL_ISUPPORTS(nsUrlClassifierClassifyCallback,
    1393             :                   nsIUrlClassifierCallback,
    1394             :                   nsIUrlClassifierClassifyCallback)
    1395             : 
    1396             : NS_IMETHODIMP
    1397           0 : nsUrlClassifierClassifyCallback::HandleEvent(const nsACString& tables)
    1398             : {
    1399           0 :   nsresult response = TablesToResponse(tables);
    1400           0 :   ClassifyMatchedInfo* matchedInfo = nullptr;
    1401             : 
    1402           0 :   if (NS_FAILED(response)) {
    1403             :     // Filter all matched info which has correct response
    1404             :     // In the case multiple tables found, use the higher priority provider
    1405           0 :     nsTArray<ClassifyMatchedInfo> matches;
    1406           0 :     for (uint32_t i = 0; i < mMatchedArray.Length(); i++) {
    1407           0 :       if (mMatchedArray[i].errorCode == response &&
    1408           0 :           (!matchedInfo ||
    1409           0 :            matchedInfo->provider.priority < mMatchedArray[i].provider.priority)) {
    1410           0 :         matchedInfo = &mMatchedArray[i];
    1411             :       }
    1412             :     }
    1413             :   }
    1414             : 
    1415           0 :   nsCString provider = matchedInfo ? matchedInfo->provider.name : EmptyCString();
    1416           0 :   nsCString prefix = matchedInfo ? matchedInfo->prefix : EmptyCString();
    1417           0 :   nsCString table = matchedInfo ? matchedInfo->table : EmptyCString();
    1418             : 
    1419           0 :   mCallback->OnClassifyComplete(response, table, provider, prefix);
    1420           0 :   return NS_OK;
    1421             : }
    1422             : 
    1423             : NS_IMETHODIMP
    1424           0 : nsUrlClassifierClassifyCallback::HandleResult(const nsACString& aTable,
    1425             :                                               const nsACString& aPrefix)
    1426             : {
    1427           0 :   LOG(("nsUrlClassifierClassifyCallback::HandleResult [%p, table %s prefix %s]",
    1428             :         this, PromiseFlatCString(aTable).get(), PromiseFlatCString(aPrefix).get()));
    1429             : 
    1430           0 :   if (NS_WARN_IF(aTable.IsEmpty()) || NS_WARN_IF(aPrefix.IsEmpty())) {
    1431           0 :     return NS_ERROR_INVALID_ARG;
    1432             :   }
    1433             : 
    1434           0 :   ClassifyMatchedInfo* matchedInfo = mMatchedArray.AppendElement();
    1435           0 :   matchedInfo->table = aTable;
    1436           0 :   matchedInfo->prefix = aPrefix;
    1437             : 
    1438             :   nsCOMPtr<nsIUrlClassifierUtils> urlUtil =
    1439           0 :     do_GetService(NS_URLCLASSIFIERUTILS_CONTRACTID);
    1440             : 
    1441           0 :   nsCString provider;
    1442           0 :   nsresult rv = urlUtil->GetProvider(aTable, provider);
    1443             : 
    1444           0 :   matchedInfo->provider.name = NS_SUCCEEDED(rv) ? provider : EmptyCString();
    1445           0 :   matchedInfo->provider.priority = 0;
    1446           0 :   for (uint8_t i = 0; i < ArrayLength(kBuiltInProviders); i++) {
    1447           0 :     if (kBuiltInProviders[i].name.Equals(matchedInfo->provider.name)) {
    1448           0 :       matchedInfo->provider.priority = kBuiltInProviders[i].priority;
    1449             :     }
    1450             :   }
    1451           0 :   matchedInfo->errorCode = TablesToResponse(aTable);
    1452             : 
    1453           0 :   return NS_OK;
    1454             : }
    1455             : 
    1456             : // -------------------------------------------------------------------------
    1457             : // Proxy class implementation
    1458             : 
    1459          70 : NS_IMPL_ADDREF(nsUrlClassifierDBService)
    1460          38 : NS_IMPL_RELEASE(nsUrlClassifierDBService)
    1461          56 : NS_INTERFACE_MAP_BEGIN(nsUrlClassifierDBService)
    1462             :   // Only nsIURIClassifier is supported in the content process!
    1463          56 :   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIUrlClassifierDBService, XRE_IsParentProcess())
    1464          51 :   NS_INTERFACE_MAP_ENTRY(nsIURIClassifier)
    1465          43 :   NS_INTERFACE_MAP_ENTRY(nsIUrlClassifierInfo)
    1466          43 :   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIObserver, XRE_IsParentProcess())
    1467          32 :   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIURIClassifier)
    1468          16 : NS_INTERFACE_MAP_END
    1469             : 
    1470             : /* static */ nsUrlClassifierDBService*
    1471           1 : nsUrlClassifierDBService::GetInstance(nsresult *result)
    1472             : {
    1473           1 :   *result = NS_OK;
    1474           1 :   if (!sUrlClassifierDBService) {
    1475           1 :     sUrlClassifierDBService = new nsUrlClassifierDBService();
    1476           1 :     if (!sUrlClassifierDBService) {
    1477           0 :       *result = NS_ERROR_OUT_OF_MEMORY;
    1478           0 :       return nullptr;
    1479             :     }
    1480             : 
    1481           1 :     NS_ADDREF(sUrlClassifierDBService);   // addref the global
    1482             : 
    1483           1 :     *result = sUrlClassifierDBService->Init();
    1484           1 :     if (NS_FAILED(*result)) {
    1485           0 :       NS_RELEASE(sUrlClassifierDBService);
    1486           0 :       return nullptr;
    1487             :     }
    1488             :   } else {
    1489             :     // Already exists, just add a ref
    1490           0 :     NS_ADDREF(sUrlClassifierDBService);   // addref the return result
    1491             :   }
    1492           1 :   return sUrlClassifierDBService;
    1493             : }
    1494             : 
    1495             : 
    1496           1 : nsUrlClassifierDBService::nsUrlClassifierDBService()
    1497             :  : mCheckMalware(CHECK_MALWARE_DEFAULT)
    1498             :  , mCheckPhishing(CHECK_PHISHING_DEFAULT)
    1499             :  , mCheckBlockedURIs(CHECK_BLOCKED_DEFAULT)
    1500          13 :  , mInUpdate(false)
    1501             : {
    1502           1 : }
    1503             : 
    1504           0 : nsUrlClassifierDBService::~nsUrlClassifierDBService()
    1505             : {
    1506           0 :   sUrlClassifierDBService = nullptr;
    1507           0 : }
    1508             : 
    1509             : void
    1510           8 : AppendTables(const nsCString& aTables, nsCString &outTables)
    1511             : {
    1512           8 :   if (!aTables.IsEmpty()) {
    1513           8 :     if (!outTables.IsEmpty()) {
    1514           7 :       outTables.Append(',');
    1515             :     }
    1516           8 :     outTables.Append(aTables);
    1517             :   }
    1518           8 : }
    1519             : 
    1520             : nsresult
    1521           1 : nsUrlClassifierDBService::ReadTablesFromPrefs()
    1522             : {
    1523           1 :   mCheckMalware = Preferences::GetBool(CHECK_MALWARE_PREF,
    1524             :     CHECK_MALWARE_DEFAULT);
    1525           1 :   mCheckPhishing = Preferences::GetBool(CHECK_PHISHING_PREF,
    1526             :     CHECK_PHISHING_DEFAULT);
    1527           1 :   mCheckBlockedURIs = Preferences::GetBool(CHECK_BLOCKED_PREF,
    1528             :     CHECK_BLOCKED_DEFAULT);
    1529             : 
    1530           2 :   nsCString allTables;
    1531           2 :   nsCString tables;
    1532             : 
    1533           1 :   mBaseTables.Truncate();
    1534           1 :   mTrackingProtectionTables.Truncate();
    1535             : 
    1536           1 :   Preferences::GetCString(PHISH_TABLE_PREF, &allTables);
    1537           1 :   if (mCheckPhishing) {
    1538           0 :     AppendTables(allTables, mBaseTables);
    1539             :   }
    1540             : 
    1541           1 :   Preferences::GetCString(MALWARE_TABLE_PREF, &tables);
    1542           1 :   AppendTables(tables, allTables);
    1543           1 :   if (mCheckMalware) {
    1544           0 :     AppendTables(tables, mBaseTables);
    1545             :   }
    1546             : 
    1547           1 :   Preferences::GetCString(BLOCKED_TABLE_PREF, &tables);
    1548           1 :   AppendTables(tables, allTables);
    1549           1 :   if (mCheckBlockedURIs) {
    1550           0 :     AppendTables(tables, mBaseTables);
    1551             :   }
    1552             : 
    1553           1 :   Preferences::GetCString(DOWNLOAD_BLOCK_TABLE_PREF, &tables);
    1554           1 :   AppendTables(tables, allTables);
    1555             : 
    1556           1 :   Preferences::GetCString(DOWNLOAD_ALLOW_TABLE_PREF, &tables);
    1557           1 :   AppendTables(tables, allTables);
    1558             : 
    1559           1 :   Preferences::GetCString(TRACKING_TABLE_PREF, &tables);
    1560           1 :   AppendTables(tables, allTables);
    1561           1 :   AppendTables(tables, mTrackingProtectionTables);
    1562             : 
    1563           1 :   Preferences::GetCString(TRACKING_WHITELIST_TABLE_PREF, &tables);
    1564           1 :   AppendTables(tables, allTables);
    1565           1 :   AppendTables(tables, mTrackingProtectionTables);
    1566             : 
    1567           1 :   Classifier::SplitTables(allTables, mGethashTables);
    1568             : 
    1569           1 :   Preferences::GetCString(DISALLOW_COMPLETION_TABLE_PREF, &tables);
    1570           1 :   Classifier::SplitTables(tables, mDisallowCompletionsTables);
    1571             : 
    1572           2 :   return NS_OK;
    1573             : }
    1574             : 
    1575             : nsresult
    1576           1 : nsUrlClassifierDBService::Init()
    1577             : {
    1578           1 :   MOZ_ASSERT(NS_IsMainThread(), "Must initialize DB service on main thread");
    1579           2 :   nsCOMPtr<nsIXULRuntime> appInfo = do_GetService("@mozilla.org/xre/app-info;1");
    1580           1 :   if (appInfo) {
    1581           1 :     bool inSafeMode = false;
    1582           1 :     appInfo->GetInSafeMode(&inSafeMode);
    1583           1 :     if (inSafeMode) {
    1584           0 :       return NS_ERROR_NOT_AVAILABLE;
    1585             :     }
    1586             :   }
    1587             : 
    1588           1 :   switch (XRE_GetProcessType()) {
    1589             :   case GeckoProcessType_Default:
    1590             :     // The parent process is supported.
    1591           1 :     break;
    1592             :   case GeckoProcessType_Content:
    1593             :     // In a content process, we simply forward all requests to the parent process,
    1594             :     // so we can skip the initialization steps here.
    1595             :     // Note that since we never register an observer, Shutdown() will also never
    1596             :     // be called in the content process.
    1597           0 :     return NS_OK;
    1598             :   default:
    1599             :     // No other process type is supported!
    1600           0 :     return NS_ERROR_NOT_AVAILABLE;
    1601             :   }
    1602             : 
    1603           1 :   sGethashNoise = Preferences::GetUint(GETHASH_NOISE_PREF,
    1604             :     GETHASH_NOISE_DEFAULT);
    1605           1 :   ReadTablesFromPrefs();
    1606             :   nsresult rv;
    1607             : 
    1608             :   {
    1609             :     // Force PSM loading on main thread
    1610           2 :     nsCOMPtr<nsICryptoHash> dummy = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
    1611           1 :     NS_ENSURE_SUCCESS(rv, rv);
    1612             :   }
    1613             : 
    1614             :   {
    1615             :     // Force nsIUrlClassifierUtils loading on main thread.
    1616             :     nsCOMPtr<nsIUrlClassifierUtils> dummy =
    1617           2 :       do_GetService(NS_URLCLASSIFIERUTILS_CONTRACTID, &rv);
    1618           1 :     NS_ENSURE_SUCCESS(rv, rv);
    1619             :   }
    1620             : 
    1621             :   // Directory providers must also be accessed on the main thread.
    1622           2 :   nsCOMPtr<nsIFile> cacheDir;
    1623           1 :   rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR,
    1624           2 :                               getter_AddRefs(cacheDir));
    1625           1 :   if (NS_FAILED(rv)) {
    1626           0 :     rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
    1627           0 :                                 getter_AddRefs(cacheDir));
    1628           0 :     if (NS_FAILED(rv)) {
    1629           0 :       return rv;
    1630             :     }
    1631             :   }
    1632             : 
    1633             :   // Start the background thread.
    1634           1 :   rv = NS_NewNamedThread("URL Classifier", &gDbBackgroundThread);
    1635           1 :   if (NS_FAILED(rv))
    1636           0 :     return rv;
    1637             : 
    1638           1 :   mWorker = new nsUrlClassifierDBServiceWorker();
    1639           1 :   if (!mWorker)
    1640           0 :     return NS_ERROR_OUT_OF_MEMORY;
    1641             : 
    1642           1 :   rv = mWorker->Init(sGethashNoise, cacheDir, this);
    1643           1 :   if (NS_FAILED(rv)) {
    1644           0 :     mWorker = nullptr;
    1645           0 :     return rv;
    1646             :   }
    1647             : 
    1648             :   // Proxy for calling the worker on the background thread
    1649           2 :   mWorkerProxy = new UrlClassifierDBServiceWorkerProxy(mWorker);
    1650           1 :   rv = mWorkerProxy->OpenDb();
    1651           1 :   if (NS_FAILED(rv)) {
    1652           0 :     return rv;
    1653             :   }
    1654             : 
    1655             :   // Add an observer for shutdown
    1656             :   nsCOMPtr<nsIObserverService> observerService =
    1657           2 :       mozilla::services::GetObserverService();
    1658           1 :   if (!observerService)
    1659           0 :     return NS_ERROR_FAILURE;
    1660             : 
    1661             :   // The application is about to quit
    1662           1 :   observerService->AddObserver(this, "quit-application", false);
    1663           1 :   observerService->AddObserver(this, "profile-before-change", false);
    1664             : 
    1665             :   // XXX: Do we *really* need to be able to change all of these at runtime?
    1666             :   // Note: These observers should only be added when everything else above has
    1667             :   //       succeeded. Failing to do so can cause long shutdown times in certain
    1668             :   //       situations. See Bug 1247798 and Bug 1244803.
    1669             :   Preferences::AddUintVarCache(&sGethashNoise, GETHASH_NOISE_PREF,
    1670           1 :     GETHASH_NOISE_DEFAULT);
    1671             : 
    1672          12 :   for (uint8_t i = 0; i < kObservedPrefs.Length(); i++) {
    1673          11 :     Preferences::AddStrongObserver(this, kObservedPrefs[i].get());
    1674             :   }
    1675             : 
    1676           1 :   return NS_OK;
    1677             : }
    1678             : 
    1679             : // nsChannelClassifier is the only consumer of this interface.
    1680             : NS_IMETHODIMP
    1681           4 : nsUrlClassifierDBService::Classify(nsIPrincipal* aPrincipal,
    1682             :                                    nsIEventTarget* aEventTarget,
    1683             :                                    bool aTrackingProtectionEnabled,
    1684             :                                    nsIURIClassifierCallback* c,
    1685             :                                    bool* result)
    1686             : {
    1687           4 :   NS_ENSURE_ARG(aPrincipal);
    1688             : 
    1689           4 :   if (XRE_IsContentProcess()) {
    1690             :     using namespace mozilla::dom;
    1691             : 
    1692           0 :     ContentChild* content = ContentChild::GetSingleton();
    1693           0 :     MOZ_ASSERT(content);
    1694             : 
    1695             :     auto actor = static_cast<URLClassifierChild*>
    1696           0 :       (content->AllocPURLClassifierChild(IPC::Principal(aPrincipal),
    1697             :                                          aTrackingProtectionEnabled,
    1698           0 :                                          result));
    1699           0 :     MOZ_ASSERT(actor);
    1700             : 
    1701           0 :     if (aEventTarget) {
    1702           0 :       content->SetEventTargetForActor(actor, aEventTarget);
    1703             :     } else {
    1704             :       // In the case null event target we should use systemgroup event target
    1705           0 :       NS_WARNING(("Null event target, we should use SystemGroup to do labelling"));
    1706             :       nsCOMPtr<nsIEventTarget> systemGroupEventTarget
    1707           0 :         = mozilla::SystemGroup::EventTargetFor(mozilla::TaskCategory::Other);
    1708           0 :       content->SetEventTargetForActor(actor, systemGroupEventTarget);
    1709             :     }
    1710           0 :     if (!content->SendPURLClassifierConstructor(actor, IPC::Principal(aPrincipal),
    1711             :                   aTrackingProtectionEnabled,
    1712             :                   result)) {
    1713           0 :       *result = false;
    1714           0 :       return NS_ERROR_FAILURE;
    1715             :     }
    1716             : 
    1717           0 :     actor->SetCallback(c);
    1718           0 :     return NS_OK;
    1719             :   }
    1720             : 
    1721           4 :   NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
    1722             : 
    1723           8 :   if (!(mCheckMalware || mCheckPhishing || aTrackingProtectionEnabled ||
    1724           4 :         mCheckBlockedURIs)) {
    1725           4 :     *result = false;
    1726           4 :     return NS_OK;
    1727             :   }
    1728             : 
    1729             :   RefPtr<nsUrlClassifierClassifyCallback> callback =
    1730           0 :     new nsUrlClassifierClassifyCallback(c);
    1731             : 
    1732           0 :   if (!callback) return NS_ERROR_OUT_OF_MEMORY;
    1733             : 
    1734           0 :   nsCString tables = mBaseTables;
    1735           0 :   if (aTrackingProtectionEnabled) {
    1736           0 :     AppendTables(mTrackingProtectionTables, tables);
    1737             :   }
    1738             : 
    1739           0 :   nsresult rv = LookupURI(aPrincipal, tables, callback, false, result);
    1740           0 :   if (rv == NS_ERROR_MALFORMED_URI) {
    1741           0 :     *result = false;
    1742             :     // The URI had no hostname, don't try to classify it.
    1743           0 :     return NS_OK;
    1744             :   }
    1745           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1746             : 
    1747           0 :   return NS_OK;
    1748             : }
    1749             : 
    1750             : NS_IMETHODIMP
    1751           0 : nsUrlClassifierDBService::ClassifyLocal(nsIURI *aURI,
    1752             :                                         const nsACString& aTables,
    1753             :                                         nsACString& aTableResults)
    1754             : {
    1755           0 :   nsTArray<nsCString> results;
    1756           0 :   ClassifyLocalWithTables(aURI, aTables, results);
    1757             : 
    1758             :   // Convert the result array to a comma separated string
    1759           0 :   aTableResults.AssignLiteral("");
    1760           0 :   bool first = true;
    1761           0 :   for (nsCString& result : results) {
    1762           0 :     if (first) {
    1763           0 :       first = false;
    1764             :     } else {
    1765           0 :       aTableResults.AppendLiteral(",");
    1766             :     }
    1767           0 :     aTableResults.Append(result);
    1768             :   }
    1769           0 :   return NS_OK;
    1770             : }
    1771             : 
    1772             : NS_IMETHODIMP
    1773           1 : nsUrlClassifierDBService::AsyncClassifyLocalWithTables(nsIURI *aURI,
    1774             :                                                        const nsACString& aTables,
    1775             :                                                        nsIURIClassifierCallback* aCallback)
    1776             : {
    1777           1 :   MOZ_ASSERT(NS_IsMainThread(), "AsyncClassifyLocalWithTables must be called "
    1778             :                                 "on main thread");
    1779             : 
    1780           1 :   if (XRE_IsContentProcess()) {
    1781             :     using namespace mozilla::dom;
    1782             :     using namespace mozilla::ipc;
    1783             : 
    1784           0 :     ContentChild* content = ContentChild::GetSingleton();
    1785           0 :     MOZ_ASSERT(content);
    1786             : 
    1787           0 :     auto actor = new URLClassifierLocalChild();
    1788             : 
    1789             :     // TODO: Bug 1353701 - Supports custom event target for labelling.
    1790             :     nsCOMPtr<nsIEventTarget> systemGroupEventTarget
    1791           0 :       = mozilla::SystemGroup::EventTargetFor(mozilla::TaskCategory::Other);
    1792           0 :     content->SetEventTargetForActor(actor, systemGroupEventTarget);
    1793             : 
    1794           0 :     URIParams uri;
    1795           0 :     SerializeURI(aURI, uri);
    1796           0 :     nsAutoCString tables(aTables);
    1797           0 :     if (!content->SendPURLClassifierLocalConstructor(actor, uri, tables)) {
    1798           0 :       return NS_ERROR_FAILURE;
    1799             :     }
    1800             : 
    1801           0 :     actor->SetCallback(aCallback);
    1802           0 :     return NS_OK;
    1803             :   }
    1804             : 
    1805           1 :   if (gShuttingDownThread) {
    1806           0 :     return NS_ERROR_ABORT;
    1807             :   }
    1808             : 
    1809             :   using namespace mozilla::Telemetry;
    1810           1 :   auto startTime = TimeStamp::Now(); // For telemetry.
    1811             : 
    1812           2 :   nsCOMPtr<nsIURI> uri = NS_GetInnermostURI(aURI);
    1813           1 :   NS_ENSURE_TRUE(uri, NS_ERROR_FAILURE);
    1814             : 
    1815           2 :   nsAutoCString key;
    1816             :   // Canonicalize the url
    1817             :   nsCOMPtr<nsIUrlClassifierUtils> utilsService =
    1818           2 :     do_GetService(NS_URLCLASSIFIERUTILS_CONTRACTID);
    1819           1 :   nsresult rv = utilsService->GetKeyForURI(uri, key);
    1820           1 :   NS_ENSURE_SUCCESS(rv, rv);
    1821             : 
    1822           2 :   auto worker = mWorker;
    1823           2 :   nsCString tables(aTables);
    1824             : 
    1825             :   // Since aCallback will be passed around threads...
    1826             :   nsMainThreadPtrHandle<nsIURIClassifierCallback> callback(
    1827             :     new nsMainThreadPtrHolder<nsIURIClassifierCallback>(
    1828           2 :       "nsIURIClassifierCallback", aCallback));
    1829             : 
    1830           3 :   nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
    1831             :     "nsUrlClassifierDBService::AsyncClassifyLocalWithTables",
    1832           4 :     [worker, key, tables, callback, startTime]() -> void {
    1833             : 
    1834           2 :       nsCString matchedLists;
    1835           2 :       nsAutoPtr<LookupResultArray> results(new LookupResultArray());
    1836           1 :       if (results) {
    1837           1 :         nsresult rv = worker->DoLocalLookup(key, tables, results);
    1838           1 :         if (NS_SUCCEEDED(rv)) {
    1839           1 :           for (uint32_t i = 0; i < results->Length(); i++) {
    1840           0 :             if (i > 0) {
    1841           0 :               matchedLists.AppendLiteral(",");
    1842             :             }
    1843           0 :             matchedLists.Append(results->ElementAt(i).mTableName);
    1844             :           }
    1845             :         }
    1846             :       }
    1847             : 
    1848           3 :       nsCOMPtr<nsIRunnable> cbRunnable = NS_NewRunnableFunction(
    1849             :         "nsUrlClassifierDBService::AsyncClassifyLocalWithTables",
    1850           4 :         [callback, matchedLists, startTime]() -> void {
    1851             :           // Measure the time diff between calling and callback.
    1852           1 :           AccumulateDelta_impl<Millisecond>::compute(
    1853           1 :             Telemetry::URLCLASSIFIER_ASYNC_CLASSIFYLOCAL_TIME, startTime);
    1854             : 
    1855             :           // |callback| is captured as const value so ...
    1856           1 :           auto cb = const_cast<nsIURIClassifierCallback*>(callback.get());
    1857           2 :           cb->OnClassifyComplete(NS_OK, // Not used.
    1858             :                                  matchedLists,
    1859           1 :                                  EmptyCString(),  // provider. (Not used)
    1860           2 :                                  EmptyCString()); // prefix. (Not used)
    1861           4 :         });
    1862             : 
    1863           1 :       NS_DispatchToMainThread(cbRunnable);
    1864           4 :     });
    1865             : 
    1866           1 :   return gDbBackgroundThread->Dispatch(r, NS_DISPATCH_NORMAL);
    1867             : }
    1868             : 
    1869             : NS_IMETHODIMP
    1870           0 : nsUrlClassifierDBService::ClassifyLocalWithTables(nsIURI *aURI,
    1871             :                                                   const nsACString& aTables,
    1872             :                                                   nsTArray<nsCString>& aTableResults)
    1873             : {
    1874           0 :   MOZ_ASSERT(NS_IsMainThread(), "ClassifyLocalWithTables must be on main thread");
    1875           0 :   if (gShuttingDownThread) {
    1876           0 :     return NS_ERROR_ABORT;
    1877             :   }
    1878             : 
    1879             :   nsresult rv;
    1880           0 :   if (XRE_IsContentProcess()) {
    1881             :     using namespace mozilla::dom;
    1882             :     using namespace mozilla::ipc;
    1883           0 :     URIParams uri;
    1884           0 :     SerializeURI(aURI, uri);
    1885           0 :     nsAutoCString tables(aTables);
    1886           0 :     bool result = ContentChild::GetSingleton()->SendClassifyLocal(uri, tables,
    1887             :                                                                   &rv,
    1888           0 :                                                                   &aTableResults);
    1889           0 :     if (result) {
    1890           0 :       return rv;
    1891             :     }
    1892           0 :     return NS_ERROR_FAILURE;
    1893             :   }
    1894             : 
    1895           0 :   AUTO_PROFILER_LABEL("nsUrlClassifierDBService::ClassifyLocalWithTables",
    1896             :                       OTHER);
    1897           0 :   Telemetry::AutoTimer<Telemetry::URLCLASSIFIER_CLASSIFYLOCAL_TIME> timer;
    1898             : 
    1899           0 :   nsCOMPtr<nsIURI> uri = NS_GetInnermostURI(aURI);
    1900           0 :   NS_ENSURE_TRUE(uri, NS_ERROR_FAILURE);
    1901             : 
    1902           0 :   nsAutoCString key;
    1903             :   // Canonicalize the url
    1904             :   nsCOMPtr<nsIUrlClassifierUtils> utilsService =
    1905           0 :     do_GetService(NS_URLCLASSIFIERUTILS_CONTRACTID);
    1906           0 :   rv = utilsService->GetKeyForURI(uri, key);
    1907           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1908             : 
    1909           0 :   nsAutoPtr<LookupResultArray> results(new LookupResultArray());
    1910           0 :   if (!results) {
    1911           0 :     return NS_ERROR_OUT_OF_MEMORY;
    1912             :   }
    1913             : 
    1914             :   // In unittests, we may not have been initalized, so don't crash.
    1915           0 :   rv = mWorkerProxy->DoLocalLookup(key, aTables, results);
    1916           0 :   if (NS_SUCCEEDED(rv)) {
    1917           0 :     rv = ProcessLookupResults(results, aTableResults);
    1918           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1919             :   }
    1920           0 :   return NS_OK;
    1921             : }
    1922             : 
    1923             : NS_IMETHODIMP
    1924           0 : nsUrlClassifierDBService::Lookup(nsIPrincipal* aPrincipal,
    1925             :                                  const nsACString& tables,
    1926             :                                  nsIUrlClassifierCallback* c)
    1927             : {
    1928           0 :   NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
    1929             : 
    1930             :   bool dummy;
    1931           0 :   return LookupURI(aPrincipal, tables, c, true, &dummy);
    1932             : }
    1933             : 
    1934             : nsresult
    1935           0 : nsUrlClassifierDBService::LookupURI(nsIPrincipal* aPrincipal,
    1936             :                                     const nsACString& tables,
    1937             :                                     nsIUrlClassifierCallback* c,
    1938             :                                     bool forceLookup,
    1939             :                                     bool *didLookup)
    1940             : {
    1941           0 :   NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
    1942           0 :   NS_ENSURE_ARG(aPrincipal);
    1943             : 
    1944           0 :   if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
    1945           0 :     *didLookup = false;
    1946           0 :     return NS_OK;
    1947             :   }
    1948             : 
    1949           0 :   nsCOMPtr<nsIURI> uri;
    1950           0 :   nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri));
    1951           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1952           0 :   NS_ENSURE_TRUE(uri, NS_ERROR_FAILURE);
    1953             : 
    1954           0 :   uri = NS_GetInnermostURI(uri);
    1955           0 :   NS_ENSURE_TRUE(uri, NS_ERROR_FAILURE);
    1956             : 
    1957           0 :   nsAutoCString key;
    1958             :   // Canonicalize the url
    1959             :   nsCOMPtr<nsIUrlClassifierUtils> utilsService =
    1960           0 :     do_GetService(NS_URLCLASSIFIERUTILS_CONTRACTID);
    1961           0 :   rv = utilsService->GetKeyForURI(uri, key);
    1962           0 :   if (NS_FAILED(rv))
    1963           0 :     return rv;
    1964             : 
    1965           0 :   if (forceLookup) {
    1966           0 :     *didLookup = true;
    1967             :   } else {
    1968           0 :     bool clean = false;
    1969             : 
    1970           0 :     if (!clean) {
    1971             :       nsCOMPtr<nsIPermissionManager> permissionManager =
    1972           0 :         services::GetPermissionManager();
    1973             : 
    1974           0 :       if (permissionManager) {
    1975             :         uint32_t perm;
    1976           0 :         rv = permissionManager->TestPermissionFromPrincipal(aPrincipal,
    1977           0 :                                                            "safe-browsing", &perm);
    1978           0 :         NS_ENSURE_SUCCESS(rv, rv);
    1979             : 
    1980           0 :         clean |= (perm == nsIPermissionManager::ALLOW_ACTION);
    1981             :       }
    1982             :     }
    1983             : 
    1984           0 :     *didLookup = !clean;
    1985           0 :     if (clean) {
    1986           0 :       return NS_OK;
    1987             :     }
    1988             :   }
    1989             : 
    1990             :   // Create an nsUrlClassifierLookupCallback object.  This object will
    1991             :   // take care of confirming partial hash matches if necessary before
    1992             :   // calling the client's callback.
    1993             :   nsCOMPtr<nsIUrlClassifierLookupCallback> callback =
    1994           0 :     new nsUrlClassifierLookupCallback(this, c);
    1995           0 :   if (!callback)
    1996           0 :     return NS_ERROR_OUT_OF_MEMORY;
    1997             : 
    1998             :   nsCOMPtr<nsIUrlClassifierLookupCallback> proxyCallback =
    1999           0 :     new UrlClassifierLookupCallbackProxy(callback);
    2000             : 
    2001             :   // Queue this lookup and call the lookup function to flush the queue if
    2002             :   // necessary.
    2003           0 :   rv = mWorker->QueueLookup(key, tables, proxyCallback);
    2004           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2005             : 
    2006             :   // This seems to just call HandlePendingLookups.
    2007           0 :   nsAutoCString dummy;
    2008           0 :   return mWorkerProxy->Lookup(nullptr, dummy, nullptr);
    2009             : }
    2010             : 
    2011             : NS_IMETHODIMP
    2012           2 : nsUrlClassifierDBService::GetTables(nsIUrlClassifierCallback* c)
    2013             : {
    2014           2 :   NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
    2015             : 
    2016             :   // The proxy callback uses the current thread.
    2017             :   nsCOMPtr<nsIUrlClassifierCallback> proxyCallback =
    2018           4 :     new UrlClassifierCallbackProxy(c);
    2019             : 
    2020           2 :   return mWorkerProxy->GetTables(proxyCallback);
    2021             : }
    2022             : 
    2023             : NS_IMETHODIMP
    2024           0 : nsUrlClassifierDBService::SetHashCompleter(const nsACString &tableName,
    2025             :                                            nsIUrlClassifierHashCompleter *completer)
    2026             : {
    2027           0 :   if (completer) {
    2028           0 :     mCompleters.Put(tableName, completer);
    2029             :   } else {
    2030           0 :     mCompleters.Remove(tableName);
    2031             :   }
    2032           0 :   ClearLastResults();
    2033           0 :   return NS_OK;
    2034             : }
    2035             : 
    2036             : NS_IMETHODIMP
    2037           0 : nsUrlClassifierDBService::ClearLastResults()
    2038             : {
    2039           0 :   NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
    2040             : 
    2041           0 :   return mWorkerProxy->ClearLastResults();
    2042             : }
    2043             : 
    2044             : NS_IMETHODIMP
    2045           2 : nsUrlClassifierDBService::BeginUpdate(nsIUrlClassifierUpdateObserver *observer,
    2046             :                                       const nsACString &updateTables)
    2047             : {
    2048           2 :   NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
    2049             : 
    2050           2 :   if (mInUpdate) {
    2051           0 :     LOG(("Already updating, not available"));
    2052           0 :     return NS_ERROR_NOT_AVAILABLE;
    2053             :   }
    2054           2 :   if (mWorker->IsBusyUpdating()) {
    2055             :     // |mInUpdate| used to work well because "notifying update observer"
    2056             :     // is synchronously done in Worker::FinishUpdate(). Even if the
    2057             :     // update observer hasn't been notified at this point, we can still
    2058             :     // dispatch BeginUpdate() since it will NOT be run until the
    2059             :     // previous Worker::FinishUpdate() returns.
    2060             :     //
    2061             :     // However, some tasks in Worker::FinishUpdate() have been moved to
    2062             :     // another thread. The update observer will NOT be notified when
    2063             :     // Worker::FinishUpdate() returns. If we only check |mInUpdate|,
    2064             :     // the following sequence might happen on worker thread:
    2065             :     //
    2066             :     // Worker::FinishUpdate() // for update 1
    2067             :     // Worker::BeginUpdate()  // for update 2
    2068             :     // Worker::NotifyUpdateObserver() // for update 1
    2069             :     //
    2070             :     // So, we have to find out a way to reject BeginUpdate() right here
    2071             :     // if the previous update observer hasn't been notified.
    2072             :     //
    2073             :     // Directly probing the worker's state is the most lightweight solution.
    2074             :     // No lock is required since Worker::BeginUpdate() and
    2075             :     // Worker::NotifyUpdateObserver() are by nature mutual exclusive.
    2076             :     // (both run on worker thread.)
    2077           0 :     LOG(("The previous update observer hasn't been notified."));
    2078           0 :     return NS_ERROR_NOT_AVAILABLE;
    2079             :   }
    2080             : 
    2081           2 :   mInUpdate = true;
    2082             : 
    2083             :   // The proxy observer uses the current thread
    2084             :   nsCOMPtr<nsIUrlClassifierUpdateObserver> proxyObserver =
    2085           4 :     new UrlClassifierUpdateObserverProxy(observer);
    2086             : 
    2087           2 :   return mWorkerProxy->BeginUpdate(proxyObserver, updateTables);
    2088             : }
    2089             : 
    2090             : NS_IMETHODIMP
    2091           1 : nsUrlClassifierDBService::BeginStream(const nsACString &table)
    2092             : {
    2093           1 :   NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
    2094             : 
    2095           1 :   return mWorkerProxy->BeginStream(table);
    2096             : }
    2097             : 
    2098             : NS_IMETHODIMP
    2099           1 : nsUrlClassifierDBService::UpdateStream(const nsACString& aUpdateChunk)
    2100             : {
    2101           1 :   NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
    2102             : 
    2103           1 :   return mWorkerProxy->UpdateStream(aUpdateChunk);
    2104             : }
    2105             : 
    2106             : NS_IMETHODIMP
    2107           1 : nsUrlClassifierDBService::FinishStream()
    2108             : {
    2109           1 :   NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
    2110             : 
    2111           1 :   return mWorkerProxy->FinishStream();
    2112             : }
    2113             : 
    2114             : NS_IMETHODIMP
    2115           2 : nsUrlClassifierDBService::FinishUpdate()
    2116             : {
    2117           2 :   NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
    2118             : 
    2119           2 :   mInUpdate = false;
    2120             : 
    2121           2 :   return mWorkerProxy->FinishUpdate();
    2122             : }
    2123             : 
    2124             : 
    2125             : NS_IMETHODIMP
    2126           0 : nsUrlClassifierDBService::CancelUpdate()
    2127             : {
    2128           0 :   NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
    2129             : 
    2130           0 :   mInUpdate = false;
    2131             : 
    2132           0 :   return mWorkerProxy->CancelUpdate();
    2133             : }
    2134             : 
    2135             : NS_IMETHODIMP
    2136           0 : nsUrlClassifierDBService::ResetDatabase()
    2137             : {
    2138           0 :   NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
    2139             : 
    2140           0 :   if (mWorker->IsBusyUpdating()) {
    2141           0 :     LOG(("Failed to ResetDatabase because of the unfinished update."));
    2142           0 :     return NS_ERROR_FAILURE;
    2143             :   }
    2144             : 
    2145           0 :   return mWorkerProxy->ResetDatabase();
    2146             : }
    2147             : 
    2148             : NS_IMETHODIMP
    2149           0 : nsUrlClassifierDBService::ReloadDatabase()
    2150             : {
    2151           0 :   NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
    2152             : 
    2153           0 :   if (mWorker->IsBusyUpdating()) {
    2154           0 :     LOG(("Failed to ReloadDatabase because of the unfinished update."));
    2155           0 :     return NS_ERROR_FAILURE;
    2156             :   }
    2157             : 
    2158           0 :   return mWorkerProxy->ReloadDatabase();
    2159             : }
    2160             : 
    2161             : NS_IMETHODIMP
    2162           0 : nsUrlClassifierDBService::ClearCache()
    2163             : {
    2164           0 :   NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
    2165             : 
    2166           0 :   return mWorkerProxy->ClearCache();
    2167             : }
    2168             : 
    2169             : 
    2170             : NS_IMETHODIMP
    2171           0 : nsUrlClassifierDBService::GetCacheInfo(const nsACString& aTable,
    2172             :                                        nsIUrlClassifierCacheInfo** aCache)
    2173             : {
    2174           0 :   NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
    2175             : 
    2176           0 :   return mWorkerProxy->GetCacheInfo(aTable, aCache);
    2177             : }
    2178             : 
    2179             : nsresult
    2180           0 : nsUrlClassifierDBService::CacheCompletions(CacheResultArray *results)
    2181             : {
    2182           0 :   NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
    2183             : 
    2184           0 :   return mWorkerProxy->CacheCompletions(results);
    2185             : }
    2186             : 
    2187             : bool
    2188           0 : nsUrlClassifierDBService::CanComplete(const nsACString &aTableName)
    2189             : {
    2190           0 :   return mGethashTables.Contains(aTableName) &&
    2191           0 :     !mDisallowCompletionsTables.Contains(aTableName);
    2192             : }
    2193             : 
    2194             : bool
    2195           0 : nsUrlClassifierDBService::GetCompleter(const nsACString &tableName,
    2196             :                                        nsIUrlClassifierHashCompleter **completer)
    2197             : {
    2198             :   // If we have specified a completer, go ahead and query it. This is only
    2199             :   // used by tests.
    2200           0 :   if (mCompleters.Get(tableName, completer)) {
    2201           0 :     return true;
    2202             :   }
    2203             : 
    2204           0 :   if (!CanComplete(tableName)) {
    2205           0 :     return false;
    2206             :   }
    2207             : 
    2208             :   // Otherwise, call gethash to find the hash completions.
    2209           0 :   return NS_SUCCEEDED(CallGetService(NS_URLCLASSIFIERHASHCOMPLETER_CONTRACTID,
    2210             :                                      completer));
    2211             : }
    2212             : 
    2213             : NS_IMETHODIMP
    2214           0 : nsUrlClassifierDBService::Observe(nsISupports *aSubject, const char *aTopic,
    2215             :                                   const char16_t *aData)
    2216             : {
    2217           0 :   if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
    2218             :     nsresult rv;
    2219           0 :     nsCOMPtr<nsIPrefBranch> prefs(do_QueryInterface(aSubject, &rv));
    2220           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2221             :     Unused << prefs;
    2222             : 
    2223           0 :     if (kObservedPrefs.Contains(NS_ConvertUTF16toUTF8(aData))) {
    2224           0 :       ReadTablesFromPrefs();
    2225             :     }
    2226           0 :   } else if (!strcmp(aTopic, "quit-application")) {
    2227             :     // Tell the update thread to finish as soon as possible.
    2228           0 :     gShuttingDownThread = true;
    2229           0 :   } else if (!strcmp(aTopic, "profile-before-change")) {
    2230           0 :     gShuttingDownThread = true;
    2231           0 :     Shutdown();
    2232             :   } else {
    2233           0 :     return NS_ERROR_UNEXPECTED;
    2234             :   }
    2235             : 
    2236           0 :   return NS_OK;
    2237             : }
    2238             : 
    2239             : // Join the background thread if it exists.
    2240             : nsresult
    2241           0 : nsUrlClassifierDBService::Shutdown()
    2242             : {
    2243           0 :   LOG(("shutting down db service\n"));
    2244           0 :   MOZ_ASSERT(XRE_IsParentProcess());
    2245             : 
    2246           0 :   if (!gDbBackgroundThread) {
    2247           0 :     return NS_OK;
    2248             :   }
    2249             : 
    2250           0 :   Telemetry::AutoTimer<Telemetry::URLCLASSIFIER_SHUTDOWN_TIME> timer;
    2251             : 
    2252           0 :   mCompleters.Clear();
    2253             : 
    2254           0 :   nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
    2255           0 :   if (prefs) {
    2256           0 :     for (uint8_t i = 0; i < kObservedPrefs.Length(); i++) {
    2257           0 :       prefs->RemoveObserver(kObservedPrefs[i].get(), this);
    2258             :     }
    2259             :   }
    2260             : 
    2261             :   // 1. Synchronize with worker thread and update thread by
    2262             :   //    *synchronously* dispatching an event to worker thread
    2263             :   //    for shutting down the update thread. The reason not
    2264             :   //    shutting down update thread directly from main thread
    2265             :   //    is to avoid racing for Classifier::mUpdateThread
    2266             :   //    between main thread and the worker thread. (Both threads
    2267             :   //    would access Classifier::mUpdateThread.)
    2268             :   using Worker = nsUrlClassifierDBServiceWorker;
    2269           0 :   RefPtr<nsIRunnable> r = NewRunnableMethod(
    2270             :     "nsUrlClassifierDBServiceWorker::FlushAndDisableAsyncUpdate",
    2271             :     mWorker,
    2272           0 :     &Worker::FlushAndDisableAsyncUpdate);
    2273           0 :   SyncRunnable::DispatchToThread(gDbBackgroundThread, r);
    2274             : 
    2275             :   // At this point the update thread has been shut down and
    2276             :   // the worker thread should only have at most one event,
    2277             :   // which is the callback event.
    2278             : 
    2279             :   // 2. Send CancelUpdate() event to notify the dangling update.
    2280             :   //    (i.e. BeginUpdate is called but FinishUpdate is not.)
    2281             :   //    and CloseDb() to clear mClassifier. They will be the last two
    2282             :   //    events on the worker thread in the shutdown process.
    2283           0 :   DebugOnly<nsresult> rv;
    2284           0 :   rv = mWorkerProxy->CancelUpdate();
    2285           0 :   MOZ_ASSERT(NS_SUCCEEDED(rv), "failed to post 'cancel update' event");
    2286           0 :   rv = mWorkerProxy->CloseDb();
    2287           0 :   MOZ_ASSERT(NS_SUCCEEDED(rv), "failed to post 'close db' event");
    2288           0 :   mWorkerProxy = nullptr;
    2289             : 
    2290             :   // 3. Invalidate XPCOM APIs by nulling out gDbBackgroundThread
    2291             :   //    since every API checks gDbBackgroundThread first. This has
    2292             :   //    to be done before calling nsIThread.shutdown because it
    2293             :   //    will cause the pending events on the joining thread to
    2294             :   //    be processed.
    2295           0 :   nsIThread *backgroundThread = nullptr;
    2296           0 :   Swap(backgroundThread, gDbBackgroundThread);
    2297             : 
    2298             :   // 4. Wait until the worker thread is down.
    2299           0 :   if (backgroundThread) {
    2300           0 :     backgroundThread->Shutdown();
    2301           0 :     NS_RELEASE(backgroundThread);
    2302             :   }
    2303             : 
    2304           0 :   mWorker = nullptr;
    2305           0 :   return NS_OK;
    2306             : }
    2307             : 
    2308             : nsIThread*
    2309          10 : nsUrlClassifierDBService::BackgroundThread()
    2310             : {
    2311          10 :   return gDbBackgroundThread;
    2312             : }
    2313             : 
    2314             : // static
    2315             : bool
    2316          33 : nsUrlClassifierDBService::ShutdownHasStarted()
    2317             : {
    2318          33 :   return gShuttingDownThread;
    2319             : }

Generated by: LCOV version 1.13