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

Generated by: LCOV version 1.13-14-ga5dd952