LCOV - code coverage report
Current view: directory - toolkit/components/url-classifier - nsUrlClassifierDBService.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 595 481 80.8 %
Date: 2012-04-21 Functions: 71 63 88.7 %

       1                 : //* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2                 : /* ***** BEGIN LICENSE BLOCK *****
       3                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       4                 :  *
       5                 :  * The contents of this file are subject to the Mozilla Public License Version
       6                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       7                 :  * the License. You may obtain a copy of the License at
       8                 :  * http://www.mozilla.org/MPL/
       9                 :  *
      10                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      11                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      12                 :  * for the specific language governing rights and limitations under the
      13                 :  * License.
      14                 :  *
      15                 :  * The Original Code is Url Classifier code
      16                 :  *
      17                 :  * The Initial Developer of the Original Code is
      18                 :  * Google Inc.
      19                 :  * Portions created by the Initial Developer are Copyright (C) 2006
      20                 :  * the Initial Developer. All Rights Reserved.
      21                 :  *
      22                 :  * Contributor(s):
      23                 :  *   Tony Chang <tony@ponderer.org> (original author)
      24                 :  *   Brett Wilson <brettw@gmail.com>
      25                 :  *   Dave Camp <dcamp@mozilla.com>
      26                 :  *   David Dahl <ddahl@mozilla.com>
      27                 :  *   Gian-Carlo Pascutto <gpascutto@mozilla.com>
      28                 :  *
      29                 :  * Alternatively, the contents of this file may be used under the terms of
      30                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      31                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      32                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      33                 :  * of those above. If you wish to allow use of your version of this file only
      34                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      35                 :  * use your version of this file under the terms of the MPL, indicate your
      36                 :  * decision by deleting the provisions above and replace them with the notice
      37                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      38                 :  * the provisions above, a recipient may use your version of this file under
      39                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      40                 :  *
      41                 :  * ***** END LICENSE BLOCK ***** */
      42                 : 
      43                 : #include "nsAutoPtr.h"
      44                 : #include "nsCOMPtr.h"
      45                 : #include "nsAppDirectoryServiceDefs.h"
      46                 : #include "nsCRT.h"
      47                 : #include "nsICryptoHash.h"
      48                 : #include "nsICryptoHMAC.h"
      49                 : #include "nsIDirectoryService.h"
      50                 : #include "nsIKeyModule.h"
      51                 : #include "nsIObserverService.h"
      52                 : #include "nsIPermissionManager.h"
      53                 : #include "nsIPrefBranch.h"
      54                 : #include "nsIPrefService.h"
      55                 : #include "nsIProperties.h"
      56                 : #include "nsToolkitCompsCID.h"
      57                 : #include "nsIUrlClassifierUtils.h"
      58                 : #include "nsUrlClassifierDBService.h"
      59                 : #include "nsUrlClassifierUtils.h"
      60                 : #include "nsUrlClassifierProxies.h"
      61                 : #include "nsURILoader.h"
      62                 : #include "nsString.h"
      63                 : #include "nsReadableUtils.h"
      64                 : #include "nsTArray.h"
      65                 : #include "nsNetUtil.h"
      66                 : #include "nsNetCID.h"
      67                 : #include "nsThreadUtils.h"
      68                 : #include "nsXPCOMStrings.h"
      69                 : #include "nsProxyRelease.h"
      70                 : #include "mozilla/Mutex.h"
      71                 : #include "mozilla/TimeStamp.h"
      72                 : #include "mozilla/Telemetry.h"
      73                 : #include "prlog.h"
      74                 : #include "prprf.h"
      75                 : #include "prnetdb.h"
      76                 : #include "Entries.h"
      77                 : #include "Classifier.h"
      78                 : #include "ProtocolParser.h"
      79                 : 
      80                 : using namespace mozilla;
      81                 : using namespace mozilla::safebrowsing;
      82                 : 
      83                 : // NSPR_LOG_MODULES=UrlClassifierDbService:5
      84                 : #if defined(PR_LOGGING)
      85                 : PRLogModuleInfo *gUrlClassifierDbServiceLog = nsnull;
      86                 : #define LOG(args) PR_LOG(gUrlClassifierDbServiceLog, PR_LOG_DEBUG, args)
      87                 : #define LOG_ENABLED() PR_LOG_TEST(gUrlClassifierDbServiceLog, 4)
      88                 : #else
      89                 : #define LOG(args)
      90                 : #define LOG_ENABLED() (false)
      91                 : #endif
      92                 : 
      93                 : // Prefs for implementing nsIURIClassifier to block page loads
      94                 : #define CHECK_MALWARE_PREF      "browser.safebrowsing.malware.enabled"
      95                 : #define CHECK_MALWARE_DEFAULT   false
      96                 : 
      97                 : #define CHECK_PHISHING_PREF     "browser.safebrowsing.enabled"
      98                 : #define CHECK_PHISHING_DEFAULT  false
      99                 : 
     100                 : #define GETHASH_NOISE_PREF      "urlclassifier.gethashnoise"
     101                 : #define GETHASH_NOISE_DEFAULT   4
     102                 : 
     103                 : #define GETHASH_TABLES_PREF     "urlclassifier.gethashtables"
     104                 : 
     105                 : #define CONFIRM_AGE_PREF        "urlclassifier.confirm-age"
     106                 : #define CONFIRM_AGE_DEFAULT_SEC (45 * 60)
     107                 : 
     108                 : class nsUrlClassifierDBServiceWorker;
     109                 : 
     110                 : // Singleton instance.
     111                 : static nsUrlClassifierDBService* sUrlClassifierDBService;
     112                 : 
     113                 : nsIThread* nsUrlClassifierDBService::gDbBackgroundThread = nsnull;
     114                 : 
     115                 : // Once we've committed to shutting down, don't do work in the background
     116                 : // thread.
     117                 : static bool gShuttingDownThread = false;
     118                 : 
     119                 : static PRInt32 gFreshnessGuarantee = CONFIRM_AGE_DEFAULT_SEC;
     120                 : 
     121                 : static void
     122              84 : SplitTables(const nsACString& str, nsTArray<nsCString>& tables)
     123                 : {
     124              84 :   tables.Clear();
     125                 : 
     126              84 :   nsACString::const_iterator begin, iter, end;
     127              84 :   str.BeginReading(begin);
     128              84 :   str.EndReading(end);
     129             335 :   while (begin != end) {
     130             167 :     iter = begin;
     131             167 :     FindCharInReadable(',', iter, end);
     132             167 :     tables.AppendElement(Substring(begin, iter));
     133             167 :     begin = iter;
     134             167 :     if (begin != end)
     135              83 :       begin++;
     136                 :   }
     137              84 : }
     138                 : 
     139                 : // -------------------------------------------------------------------------
     140                 : // Actual worker implemenatation
     141                 : class nsUrlClassifierDBServiceWorker : public nsIUrlClassifierDBServiceWorker
     142                 : {
     143                 : public:
     144                 :   nsUrlClassifierDBServiceWorker();
     145                 : 
     146                 :   NS_DECL_ISUPPORTS
     147                 :   NS_DECL_NSIURLCLASSIFIERDBSERVICE
     148                 :   NS_DECL_NSIURLCLASSIFIERDBSERVICEWORKER
     149                 : 
     150                 :   // Initialize, called in the main thread
     151                 :   nsresult Init(PRInt32 gethashNoise, nsCOMPtr<nsIFile> aCacheDir);
     152                 : 
     153                 :   // Queue a lookup for the worker to perform, called in the main thread.
     154                 :   nsresult QueueLookup(const nsACString& lookupKey,
     155                 :                        nsIUrlClassifierLookupCallback* callback);
     156                 : 
     157                 :   // Handle any queued-up lookups.  We call this function during long-running
     158                 :   // update operations to prevent lookups from blocking for too long.
     159                 :   nsresult HandlePendingLookups();
     160                 : 
     161                 : private:
     162                 :   // No subclassing
     163                 :   ~nsUrlClassifierDBServiceWorker();
     164                 : 
     165                 :   // Disallow copy constructor
     166                 :   nsUrlClassifierDBServiceWorker(nsUrlClassifierDBServiceWorker&);
     167                 : 
     168                 :   nsresult OpenDb();
     169                 : 
     170                 :   // Applies the current transaction and resets the update/working times.
     171                 :   nsresult ApplyUpdate();
     172                 : 
     173                 :   // Reset the in-progress update stream
     174                 :   void ResetStream();
     175                 : 
     176                 :  // Reset the in-progress update
     177                 :  void ResetUpdate();
     178                 : 
     179                 :   // Perform a classifier lookup for a given url.
     180                 :   nsresult DoLookup(const nsACString& spec, nsIUrlClassifierLookupCallback* c);
     181                 : 
     182                 :   // Add entries to the results.
     183                 :   nsresult AddNoise(const Prefix aPrefix,
     184                 :                     const nsCString tableName,
     185                 :                     PRInt32 aCount,
     186                 :                     LookupResultArray& results);
     187                 : 
     188                 :   nsCOMPtr<nsICryptoHash> mCryptoHash;
     189                 : 
     190                 :   nsAutoPtr<Classifier> mClassifier;
     191                 :   nsAutoPtr<ProtocolParser> mProtocolParser;
     192                 : 
     193                 :   // Directory where to store the SB databases.
     194                 :   nsCOMPtr<nsIFile> mCacheDir;
     195                 : 
     196                 :   // XXX: maybe an array of autoptrs.  Or maybe a class specifically
     197                 :   // storing a series of updates.
     198                 :   nsTArray<TableUpdate*> mTableUpdates;
     199                 : 
     200                 :   PRInt32 mUpdateWait;
     201                 : 
     202                 :   // Entries that cannot be completed. We expect them to die at
     203                 :   // the next update
     204                 :   PrefixArray mMissCache;
     205                 : 
     206                 :   nsresult mUpdateStatus;
     207                 :   nsTArray<nsCString> mUpdateTables;
     208                 : 
     209                 :   nsCOMPtr<nsIUrlClassifierUpdateObserver> mUpdateObserver;
     210                 :   bool mInStream;
     211                 : 
     212                 :   // The client key with which the data from the server will be MAC'ed.
     213                 :   nsCString mUpdateClientKey;
     214                 : 
     215                 :   // The client-specific hash key to rehash
     216                 :   PRUint32 mHashKey;
     217                 : 
     218                 :   // The number of noise entries to add to the set of lookup results.
     219                 :   PRInt32 mGethashNoise;
     220                 : 
     221                 :   // Pending lookups are stored in a queue for processing.  The queue
     222                 :   // is protected by mPendingLookupLock.
     223                 :   Mutex mPendingLookupLock;
     224                 : 
     225             560 :   class PendingLookup {
     226                 :   public:
     227                 :     TimeStamp mStartTime;
     228                 :     nsCString mKey;
     229                 :     nsCOMPtr<nsIUrlClassifierLookupCallback> mCallback;
     230                 :   };
     231                 : 
     232                 :   // list of pending lookups
     233                 :   nsTArray<PendingLookup> mPendingLookups;
     234                 : };
     235                 : 
     236            4023 : NS_IMPL_THREADSAFE_ISUPPORTS2(nsUrlClassifierDBServiceWorker,
     237                 :                               nsIUrlClassifierDBServiceWorker,
     238                 :                               nsIUrlClassifierDBService)
     239                 : 
     240               7 : nsUrlClassifierDBServiceWorker::nsUrlClassifierDBServiceWorker()
     241                 : : mInStream(false)
     242                 :   , mGethashNoise(0)
     243               7 :   , mPendingLookupLock("nsUrlClassifierDBServerWorker.mPendingLookupLock")
     244                 : {
     245               7 : }
     246                 : 
     247              14 : nsUrlClassifierDBServiceWorker::~nsUrlClassifierDBServiceWorker()
     248                 : {
     249               7 :   NS_ASSERTION(!mClassifier,
     250                 :                "Db connection not closed, leaking memory!  Call CloseDb "
     251                 :                "to close the connection.");
     252               7 : }
     253                 : 
     254                 : nsresult
     255               7 : nsUrlClassifierDBServiceWorker::Init(PRInt32 gethashNoise,
     256                 :                                      nsCOMPtr<nsIFile> aCacheDir)
     257                 : {
     258               7 :   mGethashNoise = gethashNoise;
     259               7 :   mCacheDir = aCacheDir;
     260                 : 
     261               7 :   ResetUpdate();
     262                 : 
     263               7 :   return NS_OK;
     264                 : }
     265                 : 
     266                 : nsresult
     267             140 : nsUrlClassifierDBServiceWorker::QueueLookup(const nsACString& spec,
     268                 :                                             nsIUrlClassifierLookupCallback* callback)
     269                 : {
     270             280 :   MutexAutoLock lock(mPendingLookupLock);
     271                 : 
     272             140 :   PendingLookup* lookup = mPendingLookups.AppendElement();
     273             140 :   if (!lookup) return NS_ERROR_OUT_OF_MEMORY;
     274                 : 
     275             140 :   lookup->mStartTime = TimeStamp::Now();
     276             140 :   lookup->mKey = spec;
     277             140 :   lookup->mCallback = callback;
     278                 : 
     279             140 :   return NS_OK;
     280                 : }
     281                 : 
     282                 : /**
     283                 :  * Lookup up a key in the database is a two step process:
     284                 :  *
     285                 :  * a) First we look for any Entries in the database that might apply to this
     286                 :  *    url.  For each URL there are one or two possible domain names to check:
     287                 :  *    the two-part domain name (example.com) and the three-part name
     288                 :  *    (www.example.com).  We check the database for both of these.
     289                 :  * b) If we find any entries, we check the list of fragments for that entry
     290                 :  *    against the possible subfragments of the URL as described in the
     291                 :  *    "Simplified Regular Expression Lookup" section of the protocol doc.
     292                 :  */
     293                 : nsresult
     294             140 : nsUrlClassifierDBServiceWorker::DoLookup(const nsACString& spec,
     295                 :                                          nsIUrlClassifierLookupCallback* c)
     296                 : {
     297             140 :   if (gShuttingDownThread) {
     298               0 :     c->LookupComplete(nsnull);
     299               0 :     return NS_ERROR_NOT_INITIALIZED;
     300                 :   }
     301                 : 
     302             140 :   nsresult rv = OpenDb();
     303             140 :   if (NS_FAILED(rv)) {
     304               0 :     c->LookupComplete(nsnull);
     305               0 :     return NS_ERROR_FAILURE;
     306                 :   }
     307                 : 
     308                 : #if defined(PR_LOGGING)
     309             140 :   PRIntervalTime clockStart = 0;
     310             140 :   if (LOG_ENABLED()) {
     311               0 :     clockStart = PR_IntervalNow();
     312                 :   }
     313                 : #endif
     314                 : 
     315             280 :   nsAutoPtr<LookupResultArray> results(new LookupResultArray());
     316             140 :   if (!results) {
     317               0 :     c->LookupComplete(nsnull);
     318               0 :     return NS_ERROR_OUT_OF_MEMORY;
     319                 :   }
     320                 : 
     321                 :   // we ignore failures from Check because we'd rather return the
     322                 :   // results that were found than fail.
     323             140 :   mClassifier->SetFreshTime(gFreshnessGuarantee);
     324             140 :   mClassifier->Check(spec, *results);
     325                 : 
     326             140 :   LOG(("Found %d results.", results->Length()));
     327                 : 
     328                 : #if defined(PR_LOGGING)
     329             140 :   if (LOG_ENABLED()) {
     330               0 :     PRIntervalTime clockEnd = PR_IntervalNow();
     331               0 :     LOG(("query took %dms\n",
     332                 :          PR_IntervalToMilliseconds(clockEnd - clockStart)));
     333                 :   }
     334                 : #endif
     335                 : 
     336             280 :   nsAutoPtr<LookupResultArray> completes(new LookupResultArray());
     337                 : 
     338             256 :   for (PRUint32 i = 0; i < results->Length(); i++) {
     339             116 :     if (!mMissCache.Contains(results->ElementAt(i).hash.prefix)) {
     340             115 :       completes->AppendElement(results->ElementAt(i));
     341                 :     }
     342                 :   }
     343                 : 
     344             201 :   for (PRUint32 i = 0; i < completes->Length(); i++) {
     345              99 :     if (!completes->ElementAt(i).Confirmed()) {
     346                 :       // We're going to be doing a gethash request, add some extra entries.
     347                 :       // Note that we cannot pass the first two by reference, because we
     348                 :       // add to completes, whicah can cause completes to reallocate and move.
     349              38 :       AddNoise(completes->ElementAt(i).mCodedPrefix,
     350              38 :                completes->ElementAt(i).mTableName,
     351             114 :                mGethashNoise, *completes);
     352              38 :       break;
     353                 :     }
     354                 :   }
     355                 : 
     356                 :   // At this point ownership of 'results' is handed to the callback.
     357             140 :   c->LookupComplete(completes.forget());
     358                 : 
     359             140 :   return NS_OK;
     360                 : }
     361                 : 
     362                 : nsresult
     363             227 : nsUrlClassifierDBServiceWorker::HandlePendingLookups()
     364                 : {
     365             454 :   MutexAutoLock lock(mPendingLookupLock);
     366             594 :   while (mPendingLookups.Length() > 0) {
     367             280 :     PendingLookup lookup = mPendingLookups[0];
     368             140 :     mPendingLookups.RemoveElementAt(0);
     369                 :     {
     370             280 :       MutexAutoUnlock unlock(mPendingLookupLock);
     371             140 :       DoLookup(lookup.mKey, lookup.mCallback);
     372                 :     }
     373             140 :     double lookupTime = (TimeStamp::Now() - lookup.mStartTime).ToMilliseconds();
     374                 :     Telemetry::Accumulate(Telemetry::URLCLASSIFIER_LOOKUP_TIME,
     375             140 :                           static_cast<PRUint32>(lookupTime));
     376                 :   }
     377                 : 
     378             227 :   return NS_OK;
     379                 : }
     380                 : 
     381                 : nsresult
     382              38 : nsUrlClassifierDBServiceWorker::AddNoise(const Prefix aPrefix,
     383                 :                                          const nsCString tableName,
     384                 :                                          PRInt32 aCount,
     385                 :                                          LookupResultArray& results)
     386                 : {
     387              38 :   if (aCount < 1) {
     388              38 :     return NS_OK;
     389                 :   }
     390                 : 
     391               0 :   PrefixArray noiseEntries;
     392                 :   nsresult rv = mClassifier->ReadNoiseEntries(aPrefix, tableName,
     393               0 :                                               aCount, &noiseEntries);
     394               0 :   NS_ENSURE_SUCCESS(rv, rv);
     395                 : 
     396               0 :   for (PRUint32 i = 0; i < noiseEntries.Length(); i++) {
     397               0 :     LookupResult *result = results.AppendElement();
     398               0 :     if (!result)
     399               0 :       return NS_ERROR_OUT_OF_MEMORY;
     400                 : 
     401               0 :     result->hash.prefix = noiseEntries[i];
     402               0 :     result->mNoise = true;
     403               0 :     result->mTableName.Assign(tableName);
     404                 :   }
     405                 : 
     406               0 :   return NS_OK;
     407                 : }
     408                 : 
     409                 : // Lookup a key in the db.
     410                 : NS_IMETHODIMP
     411             140 : nsUrlClassifierDBServiceWorker::Lookup(const nsACString& spec,
     412                 :                                        nsIUrlClassifierCallback* c)
     413                 : {
     414             140 :   return HandlePendingLookups();
     415                 : }
     416                 : 
     417                 : NS_IMETHODIMP
     418              48 : nsUrlClassifierDBServiceWorker::GetTables(nsIUrlClassifierCallback* c)
     419                 : {
     420              48 :   if (gShuttingDownThread)
     421               0 :     return NS_ERROR_NOT_INITIALIZED;
     422                 : 
     423              48 :   nsresult rv = OpenDb();
     424              48 :   if (NS_FAILED(rv)) {
     425               0 :     NS_ERROR("Unable to open database");
     426               0 :     return NS_ERROR_FAILURE;
     427                 :   }
     428                 : 
     429              48 :   NS_ENSURE_SUCCESS(rv, rv);
     430                 : 
     431              96 :   nsCAutoString response;
     432              48 :   mClassifier->TableRequest(response);
     433              48 :   c->HandleEvent(response);
     434                 : 
     435              48 :   return rv;
     436                 : }
     437                 : 
     438                 : void
     439               2 : nsUrlClassifierDBServiceWorker::ResetStream()
     440                 : {
     441               2 :   LOG(("ResetStream"));
     442               2 :   mInStream = false;
     443               2 :   mProtocolParser = nsnull;
     444               2 : }
     445                 : 
     446                 : void
     447               9 : nsUrlClassifierDBServiceWorker::ResetUpdate()
     448                 : {
     449               9 :   LOG(("ResetUpdate"));
     450               9 :   mUpdateWait = 0;
     451               9 :   mUpdateStatus = NS_OK;
     452               9 :   mUpdateObserver = nsnull;
     453               9 :   mUpdateClientKey.Truncate();
     454               9 : }
     455                 : 
     456                 : NS_IMETHODIMP
     457               0 : nsUrlClassifierDBServiceWorker::SetHashCompleter(const nsACString &tableName,
     458                 :                                                  nsIUrlClassifierHashCompleter *completer)
     459                 : {
     460               0 :   return NS_ERROR_NOT_IMPLEMENTED;
     461                 : }
     462                 : 
     463                 : NS_IMETHODIMP
     464              77 : nsUrlClassifierDBServiceWorker::BeginUpdate(nsIUrlClassifierUpdateObserver *observer,
     465                 :                                             const nsACString &tables,
     466                 :                                             const nsACString &clientKey)
     467                 : {
     468              77 :   LOG(("nsUrlClassifierDBServiceWorker::BeginUpdate [%s]", PromiseFlatCString(tables).get()));
     469                 : 
     470              77 :   if (gShuttingDownThread)
     471               0 :     return NS_ERROR_NOT_INITIALIZED;
     472                 : 
     473              77 :   NS_ENSURE_STATE(!mUpdateObserver);
     474                 : 
     475              77 :   nsresult rv = OpenDb();
     476              77 :   if (NS_FAILED(rv)) {
     477               0 :     NS_ERROR("Unable to open database");
     478               0 :     return NS_ERROR_FAILURE;
     479                 :   }
     480                 : 
     481              77 :   mUpdateStatus = NS_OK;
     482              77 :   mUpdateObserver = observer;
     483              77 :   SplitTables(tables, mUpdateTables);
     484                 : 
     485              77 :   if (!clientKey.IsEmpty()) {
     486               7 :     rv = nsUrlClassifierUtils::DecodeClientKey(clientKey, mUpdateClientKey);
     487               7 :     NS_ENSURE_SUCCESS(rv, rv);
     488               7 :     LOG(("clientKey present, marking update key"));
     489                 :   }
     490                 : 
     491              77 :   return NS_OK;
     492                 : }
     493                 : 
     494                 : NS_IMETHODIMP
     495              89 : nsUrlClassifierDBServiceWorker::BeginStream(const nsACString &table,
     496                 :                                             const nsACString &serverMAC)
     497                 : {
     498              89 :   LOG(("nsUrlClassifierDBServiceWorker::BeginStream"));
     499                 : 
     500              89 :   if (gShuttingDownThread)
     501               0 :     return NS_ERROR_NOT_INITIALIZED;
     502                 : 
     503              89 :   NS_ENSURE_STATE(mUpdateObserver);
     504              89 :   NS_ENSURE_STATE(!mInStream);
     505                 : 
     506              89 :   mInStream = true;
     507                 : 
     508              89 :   NS_ASSERTION(!mProtocolParser, "Should not have a protocol parser.");
     509                 : 
     510              89 :   mProtocolParser = new ProtocolParser(mHashKey);
     511              89 :   if (!mProtocolParser)
     512               0 :     return NS_ERROR_OUT_OF_MEMORY;
     513                 : 
     514              89 :   mProtocolParser->Init(mCryptoHash);
     515                 : 
     516                 :   nsresult rv;
     517                 : 
     518                 :   // If we're expecting a MAC, create the nsICryptoHMAC component now.
     519              89 :   if (!mUpdateClientKey.IsEmpty()) {
     520              11 :     LOG(("Expecting MAC in this stream"));
     521              11 :     rv = mProtocolParser->InitHMAC(mUpdateClientKey, serverMAC);
     522              11 :     NS_ENSURE_SUCCESS(rv, rv);
     523                 :   } else {
     524              78 :     LOG(("No MAC in this stream"));
     525                 :   }
     526                 : 
     527              89 :   if (!table.IsEmpty()) {
     528              12 :     mProtocolParser->SetCurrentTable(table);
     529                 :   }
     530                 : 
     531              89 :   return NS_OK;
     532                 : }
     533                 : 
     534                 : /**
     535                 :  * Updating the database:
     536                 :  *
     537                 :  * The Update() method takes a series of chunks separated with control data,
     538                 :  * as described in
     539                 :  * http://code.google.com/p/google-safe-browsing/wiki/Protocolv2Spec
     540                 :  *
     541                 :  * It will iterate through the control data until it reaches a chunk.  By
     542                 :  * the time it reaches a chunk, it should have received
     543                 :  * a) the table to which this chunk applies
     544                 :  * b) the type of chunk (add, delete, expire add, expire delete).
     545                 :  * c) the chunk ID
     546                 :  * d) the length of the chunk.
     547                 :  *
     548                 :  * For add and subtract chunks, it needs to read the chunk data (expires
     549                 :  * don't have any data).  Chunk data is a list of URI fragments whose
     550                 :  * encoding depends on the type of table (which is indicated by the end
     551                 :  * of the table name):
     552                 :  * a) tables ending with -exp are a zlib-compressed list of URI fragments
     553                 :  *    separated by newlines.
     554                 :  * b) tables ending with -sha128 have the form
     555                 :  *    [domain][N][frag0]...[fragN]
     556                 :  *       16    1   16        16
     557                 :  *    If N is 0, the domain is reused as a fragment.
     558                 :  * c) any other tables are assumed to be a plaintext list of URI fragments
     559                 :  *    separated by newlines.
     560                 :  *
     561                 :  * Update() can be fed partial data;  It will accumulate data until there is
     562                 :  * enough to act on.  Finish() should be called when there will be no more
     563                 :  * data.
     564                 :  */
     565                 : NS_IMETHODIMP
     566              87 : nsUrlClassifierDBServiceWorker::UpdateStream(const nsACString& chunk)
     567                 : {
     568              87 :   if (gShuttingDownThread)
     569               0 :     return NS_ERROR_NOT_INITIALIZED;
     570                 : 
     571              87 :   NS_ENSURE_STATE(mInStream);
     572                 : 
     573              87 :   HandlePendingLookups();
     574                 : 
     575              87 :   return mProtocolParser->AppendStream(chunk);
     576                 : }
     577                 : 
     578                 : NS_IMETHODIMP
     579              87 : nsUrlClassifierDBServiceWorker::FinishStream()
     580                 : {
     581              87 :   if (gShuttingDownThread)
     582               0 :     return NS_ERROR_NOT_INITIALIZED;
     583                 : 
     584              87 :   NS_ENSURE_STATE(mInStream);
     585              87 :   NS_ENSURE_STATE(mUpdateObserver);
     586                 : 
     587              87 :   mInStream = false;
     588                 : 
     589              87 :   mProtocolParser->FinishHMAC();
     590                 : 
     591              87 :   if (NS_SUCCEEDED(mProtocolParser->Status())) {
     592              80 :     if (mProtocolParser->UpdateWait()) {
     593              80 :       mUpdateWait = mProtocolParser->UpdateWait();
     594                 :     }
     595                 :     // XXX: Only allow forwards from the initial update?
     596                 :     const nsTArray<ProtocolParser::ForwardedUpdate> &forwards =
     597              80 :       mProtocolParser->Forwards();
     598              96 :     for (uint32 i = 0; i < forwards.Length(); i++) {
     599              16 :       const ProtocolParser::ForwardedUpdate &forward = forwards[i];
     600              16 :       mUpdateObserver->UpdateUrlRequested(forward.url, forward.table, forward.mac);
     601                 :     }
     602                 :     // Hold on to any TableUpdate objects that were created by the
     603                 :     // parser.
     604              80 :     mTableUpdates.AppendElements(mProtocolParser->GetTableUpdates());
     605              80 :     mProtocolParser->ForgetTableUpdates();
     606                 :   } else {
     607               7 :     mUpdateStatus = mProtocolParser->Status();
     608                 :   }
     609              87 :   mUpdateObserver->StreamFinished(mProtocolParser->Status(), 0);
     610                 : 
     611                 :   // Only reset if MAC was OK
     612              87 :   if (NS_SUCCEEDED(mUpdateStatus)) {
     613              80 :     if (mProtocolParser->ResetRequested()) {
     614               1 :       mClassifier->Reset();
     615                 :     }
     616                 :   }
     617                 :   // Rekey will cause update to fail (can't check MACs)
     618              87 :   if (mProtocolParser->RekeyRequested()) {
     619               1 :     mUpdateObserver->RekeyRequested();
     620                 :   }
     621                 : 
     622              87 :   mProtocolParser = nsnull;
     623              87 :   return NS_OK;
     624                 : }
     625                 : 
     626                 : NS_IMETHODIMP
     627              75 : nsUrlClassifierDBServiceWorker::FinishUpdate()
     628                 : {
     629              75 :   if (gShuttingDownThread)
     630               0 :     return NS_ERROR_NOT_INITIALIZED;
     631              75 :   NS_ENSURE_STATE(mUpdateObserver);
     632                 : 
     633              75 :   if (NS_SUCCEEDED(mUpdateStatus)) {
     634              68 :     mUpdateStatus = ApplyUpdate();
     635                 :   }
     636                 : 
     637              75 :   mMissCache.Clear();
     638                 : 
     639              75 :   if (NS_SUCCEEDED(mUpdateStatus)) {
     640              68 :     LOG(("Notifying success: %d", mUpdateWait));
     641              68 :     mUpdateObserver->UpdateSuccess(mUpdateWait);
     642                 :   } else {
     643               7 :     LOG(("Notifying error: %d", mUpdateStatus));
     644               7 :     mUpdateObserver->UpdateError(mUpdateStatus);
     645                 :     /*
     646                 :      * mark the tables as spoiled, we don't want to block hosts
     647                 :      * longer than normal because our update failed
     648                 :     */
     649               7 :     mClassifier->MarkSpoiled(mUpdateTables);
     650                 :   }
     651              75 :   mUpdateObserver = nsnull;
     652                 : 
     653              75 :   return NS_OK;
     654                 : }
     655                 : 
     656                 : nsresult
     657              68 : nsUrlClassifierDBServiceWorker::ApplyUpdate()
     658                 : {
     659              68 :   LOG(("nsUrlClassifierDBServiceWorker::ApplyUpdate()"));
     660              68 :   return mClassifier->ApplyUpdates(&mTableUpdates);
     661                 : }
     662                 : 
     663                 : NS_IMETHODIMP
     664              46 : nsUrlClassifierDBServiceWorker::ResetDatabase()
     665                 : {
     666              46 :   nsresult rv = OpenDb();
     667              46 :   NS_ENSURE_SUCCESS(rv, rv);
     668                 : 
     669              46 :   mClassifier->Reset();
     670                 : 
     671              46 :   rv = CloseDb();
     672              46 :   NS_ENSURE_SUCCESS(rv, rv);
     673                 : 
     674              46 :   return NS_OK;
     675                 : }
     676                 : 
     677                 : NS_IMETHODIMP
     678               9 : nsUrlClassifierDBServiceWorker::CancelUpdate()
     679                 : {
     680               9 :   LOG(("nsUrlClassifierDBServiceWorker::CancelUpdate"));
     681                 : 
     682               9 :   if (mUpdateObserver) {
     683               2 :     LOG(("UpdateObserver exists, cancelling"));
     684                 : 
     685               2 :     mUpdateStatus = NS_BINDING_ABORTED;
     686                 : 
     687               2 :     mUpdateObserver->UpdateError(mUpdateStatus);
     688                 : 
     689                 :     /*
     690                 :      * mark the tables as spoiled, we don't want to block hosts
     691                 :      * longer than normal because our update failed
     692                 :     */
     693               2 :     mClassifier->MarkSpoiled(mUpdateTables);
     694                 : 
     695               2 :     ResetStream();
     696               2 :     ResetUpdate();
     697                 :   } else {
     698               7 :     LOG(("No UpdateObserver, nothing to cancel"));
     699                 :   }
     700                 : 
     701               9 :   return NS_OK;
     702                 : }
     703                 : 
     704                 : // Allows the main thread to delete the connection which may be in
     705                 : // a background thread.
     706                 : // XXX This could be turned into a single shutdown event so the logic
     707                 : // is simpler in nsUrlClassifierDBService::Shutdown.
     708                 : NS_IMETHODIMP
     709              53 : nsUrlClassifierDBServiceWorker::CloseDb()
     710                 : {
     711              53 :   if (mClassifier) {
     712              50 :     mClassifier->Close();
     713              50 :     mClassifier = nsnull;
     714                 :   }
     715                 : 
     716              53 :   mCryptoHash = nsnull;
     717              53 :   LOG(("urlclassifier db closed\n"));
     718                 : 
     719              53 :   return NS_OK;
     720                 : }
     721                 : 
     722                 : NS_IMETHODIMP
     723              29 : nsUrlClassifierDBServiceWorker::CacheCompletions(CacheResultArray *results)
     724                 : {
     725              29 :   LOG(("nsUrlClassifierDBServiceWorker::CacheCompletions [%p]", this));
     726              29 :   if (!mClassifier)
     727               0 :     return NS_OK;
     728                 : 
     729                 :   // Ownership is transferred in to us
     730              58 :   nsAutoPtr<CacheResultArray> resultsPtr(results);
     731                 : 
     732              58 :   nsAutoPtr<ProtocolParser> pParse(new ProtocolParser(mHashKey));
     733              58 :   nsTArray<TableUpdate*> updates;
     734                 : 
     735                 :   // Only cache results for tables that we have, don't take
     736                 :   // in tables we might accidentally have hit during a completion.
     737                 :   // This happens due to goog vs googpub lists existing.
     738              58 :   nsTArray<nsCString> tables;
     739              29 :   nsresult rv = mClassifier->ActiveTables(tables);
     740              29 :   NS_ENSURE_SUCCESS(rv, rv);
     741                 : 
     742              76 :   for (PRUint32 i = 0; i < resultsPtr->Length(); i++) {
     743              47 :     bool activeTable = false;
     744              95 :     for (PRUint32 table = 0; table < tables.Length(); table++) {
     745              48 :       if (tables[table].Equals(resultsPtr->ElementAt(i).table)) {
     746              46 :         activeTable = true;
     747                 :       }
     748                 :     }
     749              47 :     if (activeTable) {
     750              46 :       TableUpdate * tu = pParse->GetTableUpdate(resultsPtr->ElementAt(i).table);
     751              46 :       LOG(("CacheCompletion Addchunk %d hash %X", resultsPtr->ElementAt(i).entry.addChunk,
     752                 :            resultsPtr->ElementAt(i).entry.hash.prefix));
     753              46 :       tu->NewAddComplete(resultsPtr->ElementAt(i).entry.addChunk,
     754              92 :                          resultsPtr->ElementAt(i).entry.hash.complete);
     755              46 :       tu->NewAddChunk(resultsPtr->ElementAt(i).entry.addChunk);
     756              46 :       tu->SetLocalUpdate();
     757              46 :       updates.AppendElement(tu);
     758              46 :       pParse->ForgetTableUpdates();
     759                 :     } else {
     760               1 :       LOG(("Completion received, but table is not active, so not caching."));
     761                 :     }
     762                 :    }
     763                 : 
     764              29 :   mClassifier->ApplyUpdates(&updates);
     765              29 :   return NS_OK;
     766                 : }
     767                 : 
     768                 : NS_IMETHODIMP
     769             140 : nsUrlClassifierDBServiceWorker::CacheMisses(PrefixArray *results)
     770                 : {
     771             140 :   LOG(("nsUrlClassifierDBServiceWorker::CacheMisses [%p] %d",
     772                 :        this, results->Length()));
     773                 : 
     774                 :   // Ownership is transferred in to us
     775             280 :   nsAutoPtr<PrefixArray> resultsPtr(results);
     776                 : 
     777             153 :   for (PRUint32 i = 0; i < resultsPtr->Length(); i++) {
     778              13 :     mMissCache.AppendElement(resultsPtr->ElementAt(i));
     779                 :    }
     780             140 :   return NS_OK;
     781                 : }
     782                 : 
     783                 : nsresult
     784             311 : nsUrlClassifierDBServiceWorker::OpenDb()
     785                 : {
     786                 :   // Connection already open, don't do anything.
     787             311 :   if (mClassifier) {
     788             261 :     return NS_OK;
     789                 :   }
     790                 : 
     791              50 :   LOG(("Opening db"));
     792                 : 
     793             100 :   nsAutoPtr<Classifier> classifier(new Classifier());
     794              50 :   if (!classifier) {
     795               0 :     return NS_ERROR_OUT_OF_MEMORY;
     796                 :   }
     797                 : 
     798              50 :   classifier->SetFreshTime(gFreshnessGuarantee);
     799                 : 
     800              50 :   nsresult rv = classifier->Open(*mCacheDir);
     801              50 :   if (NS_FAILED(rv)) {
     802               0 :     NS_WARNING("Failed to open URL classifier.");
     803                 :   }
     804                 : 
     805              50 :   mHashKey = classifier->GetHashKey();
     806              50 :   mClassifier = classifier;
     807                 : 
     808              50 :   mCryptoHash = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
     809              50 :   NS_ENSURE_SUCCESS(rv, rv);
     810                 : 
     811              50 :   return NS_OK;
     812                 : }
     813                 : 
     814                 : // -------------------------------------------------------------------------
     815                 : // nsUrlClassifierLookupCallback
     816                 : //
     817                 : // This class takes the results of a lookup found on the worker thread
     818                 : // and handles any necessary partial hash expansions before calling
     819                 : // the client callback.
     820                 : 
     821                 : class nsUrlClassifierLookupCallback : public nsIUrlClassifierLookupCallback
     822                 :                                     , public nsIUrlClassifierHashCompleterCallback
     823                 : {
     824                 : public:
     825                 :   NS_DECL_ISUPPORTS
     826                 :   NS_DECL_NSIURLCLASSIFIERLOOKUPCALLBACK
     827                 :   NS_DECL_NSIURLCLASSIFIERHASHCOMPLETERCALLBACK
     828                 : 
     829             140 :   nsUrlClassifierLookupCallback(nsUrlClassifierDBService *dbservice,
     830                 :                                 nsIUrlClassifierCallback *c)
     831                 :     : mDBService(dbservice)
     832                 :     , mResults(nsnull)
     833                 :     , mPendingCompletions(0)
     834             140 :     , mCallback(c)
     835             140 :     {}
     836                 : 
     837                 :   ~nsUrlClassifierLookupCallback();
     838                 : 
     839                 : private:
     840                 :   nsresult HandleResults();
     841                 : 
     842                 :   nsRefPtr<nsUrlClassifierDBService> mDBService;
     843                 :   nsAutoPtr<LookupResultArray> mResults;
     844                 : 
     845                 :   // Completed results to send back to the worker for caching.
     846                 :   nsAutoPtr<CacheResultArray> mCacheResults;
     847                 : 
     848                 :   PRUint32 mPendingCompletions;
     849                 :   nsCOMPtr<nsIUrlClassifierCallback> mCallback;
     850                 : };
     851                 : 
     852            2760 : NS_IMPL_THREADSAFE_ISUPPORTS2(nsUrlClassifierLookupCallback,
     853                 :                               nsIUrlClassifierLookupCallback,
     854                 :                               nsIUrlClassifierHashCompleterCallback)
     855                 : 
     856             280 : nsUrlClassifierLookupCallback::~nsUrlClassifierLookupCallback()
     857                 : {
     858             280 :   nsCOMPtr<nsIThread> thread;
     859             140 :   (void)NS_GetMainThread(getter_AddRefs(thread));
     860                 : 
     861             140 :   if (mCallback) {
     862             140 :     (void)NS_ProxyRelease(thread, mCallback, false);
     863                 :   }
     864             140 : }
     865                 : 
     866                 : NS_IMETHODIMP
     867             140 : nsUrlClassifierLookupCallback::LookupComplete(nsTArray<LookupResult>* results)
     868                 : {
     869             140 :   NS_ASSERTION(mResults == nsnull,
     870                 :                "Should only get one set of results per nsUrlClassifierLookupCallback!");
     871                 : 
     872             140 :   if (!results) {
     873               0 :     HandleResults();
     874               0 :     return NS_OK;
     875                 :   }
     876                 : 
     877             140 :   mResults = results;
     878                 : 
     879                 :   // Check the results entries that need to be completed.
     880             255 :   for (PRUint32 i = 0; i < results->Length(); i++) {
     881             115 :     LookupResult& result = results->ElementAt(i);
     882                 : 
     883                 :     // We will complete partial matches and matches that are stale.
     884             115 :     if (!result.Confirmed()) {
     885             108 :       nsCOMPtr<nsIUrlClassifierHashCompleter> completer;
     886             108 :       if (mDBService->GetCompleter(result.mTableName,
     887             108 :                                    getter_AddRefs(completer))) {
     888             108 :         nsCAutoString partialHash;
     889                 :         partialHash.Assign(reinterpret_cast<char*>(&result.hash.prefix),
     890              54 :                            PREFIX_SIZE);
     891                 : 
     892              54 :         nsresult rv = completer->Complete(partialHash, this);
     893              54 :         if (NS_SUCCEEDED(rv)) {
     894              54 :           mPendingCompletions++;
     895                 :         }
     896                 :       } else {
     897                 :         // For tables with no hash completer, a complete hash match is
     898                 :         // good enough, we'll consider it fresh.
     899               0 :         if (result.Complete()) {
     900               0 :           result.mFresh = true;
     901                 :         } else {
     902               0 :           NS_WARNING("Partial match in a table without a valid completer, ignoring partial match.");
     903                 :         }
     904                 :       }
     905                 :     }
     906                 :   }
     907                 : 
     908             140 :   if (mPendingCompletions == 0) {
     909                 :     // All results were complete, we're ready!
     910             102 :     HandleResults();
     911                 :   }
     912                 : 
     913             140 :   return NS_OK;
     914                 : }
     915                 : 
     916                 : NS_IMETHODIMP
     917              54 : nsUrlClassifierLookupCallback::CompletionFinished(nsresult status)
     918                 : {
     919              54 :   LOG(("nsUrlClassifierLookupCallback::CompletionFinished [%p, %08x]",
     920                 :        this, status));
     921              54 :   if (NS_FAILED(status)) {
     922               0 :     NS_WARNING("gethash response failed.");
     923                 :   }
     924                 : 
     925              54 :   mPendingCompletions--;
     926              54 :   if (mPendingCompletions == 0) {
     927              38 :     HandleResults();
     928                 :   }
     929                 : 
     930              54 :   return NS_OK;
     931                 : }
     932                 : 
     933                 : NS_IMETHODIMP
     934              48 : nsUrlClassifierLookupCallback::Completion(const nsACString& completeHash,
     935                 :                                           const nsACString& tableName,
     936                 :                                           PRUint32 chunkId,
     937                 :                                           bool verified)
     938                 : {
     939              48 :   LOG(("nsUrlClassifierLookupCallback::Completion [%p, %s, %d, %d]",
     940                 :        this, PromiseFlatCString(tableName).get(), chunkId, verified));
     941                 :   mozilla::safebrowsing::Completion hash;
     942              48 :   hash.Assign(completeHash);
     943                 : 
     944                 :   // Send this completion to the store for caching.
     945              48 :   if (!mCacheResults) {
     946              29 :     mCacheResults = new CacheResultArray();
     947              29 :     if (!mCacheResults)
     948               0 :       return NS_ERROR_OUT_OF_MEMORY;
     949                 :   }
     950                 : 
     951              48 :   if (verified) {
     952              94 :     CacheResult result;
     953              47 :     result.entry.addChunk = chunkId;
     954              47 :     result.entry.hash.complete = hash;
     955              47 :     result.table = tableName;
     956                 : 
     957                 :     // OK if this fails, we just won't cache the item.
     958              47 :     mCacheResults->AppendElement(result);
     959                 :   }
     960                 : 
     961                 :   // Check if this matched any of our results.
     962             242 :   for (PRUint32 i = 0; i < mResults->Length(); i++) {
     963             194 :     LookupResult& result = mResults->ElementAt(i);
     964                 : 
     965                 :     // Now, see if it verifies a lookup
     966             194 :     if (result.CompleteHash() == hash && result.mTableName.Equals(tableName)) {
     967              41 :       result.mProtocolConfirmed = true;
     968                 :     }
     969                 :   }
     970                 : 
     971              48 :   return NS_OK;
     972                 : }
     973                 : 
     974                 : nsresult
     975             140 : nsUrlClassifierLookupCallback::HandleResults()
     976                 : {
     977             140 :   if (!mResults) {
     978                 :     // No results, this URI is clean.
     979               0 :     return mCallback->HandleEvent(NS_LITERAL_CSTRING(""));
     980                 :   }
     981                 : 
     982             280 :   nsTArray<nsCString> tables;
     983                 :   // Build a stringified list of result tables.
     984             255 :   for (PRUint32 i = 0; i < mResults->Length(); i++) {
     985             115 :     LookupResult& result = mResults->ElementAt(i);
     986                 : 
     987                 :     // Leave out results that weren't confirmed, as their existence on
     988                 :     // the list can't be verified.  Also leave out randomly-generated
     989                 :     // noise.
     990             115 :     if (!result.Confirmed() || result.mNoise) {
     991              13 :       LOG(("Skipping result from table %s", result.mTableName.get()));
     992              13 :       continue;
     993                 :     }
     994                 : 
     995             102 :     LOG(("Confirmed result from table %s", result.mTableName.get()));
     996                 : 
     997             102 :     if (tables.IndexOf(result.mTableName) == nsTArray<nsCString>::NoIndex) {
     998              86 :       tables.AppendElement(result.mTableName);
     999                 :     }
    1000                 :   }
    1001                 : 
    1002                 :   // Some parts of this gethash request generated no hits at all.
    1003                 :   // Prefixes must have been removed from the database since our last update.
    1004                 :   // Save the prefixes we checked to prevent repeated requests
    1005                 :   // until the next update.
    1006             280 :   nsAutoPtr<PrefixArray> cacheMisses(new PrefixArray());
    1007             140 :   if (cacheMisses) {
    1008             255 :     for (uint32 i = 0; i < mResults->Length(); i++) {
    1009             115 :       LookupResult &result = mResults->ElementAt(i);
    1010             115 :       if (!result.Confirmed()) {
    1011              13 :         cacheMisses->AppendElement(result.PrefixHash());
    1012                 :       }
    1013                 :     }
    1014                 :     // Hands ownership of the miss array back to the worker thread.
    1015             140 :     mDBService->CacheMisses(cacheMisses.forget());
    1016                 :   }
    1017                 : 
    1018             140 :   if (mCacheResults) {
    1019                 :     // This hands ownership of the cache results array back to the worker
    1020                 :     // thread.
    1021              29 :     mDBService->CacheCompletions(mCacheResults.forget());
    1022                 :   }
    1023                 : 
    1024             280 :   nsCAutoString tableStr;
    1025             226 :   for (PRUint32 i = 0; i < tables.Length(); i++) {
    1026              86 :     if (i != 0)
    1027               9 :       tableStr.Append(',');
    1028              86 :     tableStr.Append(tables[i]);
    1029                 :   }
    1030                 : 
    1031             140 :   return mCallback->HandleEvent(tableStr);
    1032                 : }
    1033                 : 
    1034                 : 
    1035                 : // -------------------------------------------------------------------------
    1036                 : // Helper class for nsIURIClassifier implementation, translates table names
    1037                 : // to nsIURIClassifier enums.
    1038                 : 
    1039                 : class nsUrlClassifierClassifyCallback : public nsIUrlClassifierCallback
    1040               0 : {
    1041                 : public:
    1042                 :   NS_DECL_ISUPPORTS
    1043                 :   NS_DECL_NSIURLCLASSIFIERCALLBACK
    1044                 : 
    1045               0 :   nsUrlClassifierClassifyCallback(nsIURIClassifierCallback *c,
    1046                 :                                   bool checkMalware,
    1047                 :                                   bool checkPhishing)
    1048                 :     : mCallback(c)
    1049                 :     , mCheckMalware(checkMalware)
    1050               0 :     , mCheckPhishing(checkPhishing)
    1051               0 :     {}
    1052                 : 
    1053                 : private:
    1054                 :   nsCOMPtr<nsIURIClassifierCallback> mCallback;
    1055                 :   bool mCheckMalware;
    1056                 :   bool mCheckPhishing;
    1057                 : };
    1058                 : 
    1059               0 : NS_IMPL_THREADSAFE_ISUPPORTS1(nsUrlClassifierClassifyCallback,
    1060                 :                               nsIUrlClassifierCallback)
    1061                 : 
    1062                 : NS_IMETHODIMP
    1063               0 : nsUrlClassifierClassifyCallback::HandleEvent(const nsACString& tables)
    1064                 : {
    1065                 :   // XXX: we should probably have the wardens tell the service which table
    1066                 :   // names match with which classification.  For now the table names give
    1067                 :   // enough information.
    1068               0 :   nsresult response = NS_OK;
    1069                 : 
    1070               0 :   nsACString::const_iterator begin, end;
    1071                 : 
    1072               0 :   tables.BeginReading(begin);
    1073               0 :   tables.EndReading(end);
    1074               0 :   if (mCheckMalware &&
    1075               0 :       FindInReadable(NS_LITERAL_CSTRING("-malware-"), begin, end)) {
    1076               0 :     response = NS_ERROR_MALWARE_URI;
    1077                 :   } else {
    1078                 :     // Reset begin before checking phishing table
    1079               0 :     tables.BeginReading(begin);
    1080                 : 
    1081               0 :     if (mCheckPhishing &&
    1082               0 :         FindInReadable(NS_LITERAL_CSTRING("-phish-"), begin, end)) {
    1083               0 :       response = NS_ERROR_PHISHING_URI;
    1084                 :     }
    1085                 :   }
    1086                 : 
    1087               0 :   mCallback->OnClassifyComplete(response);
    1088                 : 
    1089               0 :   return NS_OK;
    1090                 : }
    1091                 : 
    1092                 : 
    1093                 : // -------------------------------------------------------------------------
    1094                 : // Proxy class implementation
    1095                 : 
    1096            1057 : NS_IMPL_THREADSAFE_ISUPPORTS3(nsUrlClassifierDBService,
    1097                 :                               nsIUrlClassifierDBService,
    1098                 :                               nsIURIClassifier,
    1099                 :                               nsIObserver)
    1100                 : 
    1101                 : /* static */ nsUrlClassifierDBService*
    1102               7 : nsUrlClassifierDBService::GetInstance(nsresult *result)
    1103                 : {
    1104               7 :   *result = NS_OK;
    1105               7 :   if (!sUrlClassifierDBService) {
    1106               7 :     sUrlClassifierDBService = new nsUrlClassifierDBService();
    1107               7 :     if (!sUrlClassifierDBService) {
    1108               0 :       *result = NS_ERROR_OUT_OF_MEMORY;
    1109               0 :       return nsnull;
    1110                 :     }
    1111                 : 
    1112               7 :     NS_ADDREF(sUrlClassifierDBService);   // addref the global
    1113                 : 
    1114               7 :     *result = sUrlClassifierDBService->Init();
    1115               7 :     if (NS_FAILED(*result)) {
    1116               0 :       NS_RELEASE(sUrlClassifierDBService);
    1117               0 :       return nsnull;
    1118                 :     }
    1119                 :   } else {
    1120                 :     // Already exists, just add a ref
    1121               0 :     NS_ADDREF(sUrlClassifierDBService);   // addref the return result
    1122                 :   }
    1123               7 :   return sUrlClassifierDBService;
    1124                 : }
    1125                 : 
    1126                 : 
    1127               7 : nsUrlClassifierDBService::nsUrlClassifierDBService()
    1128                 :  : mCheckMalware(CHECK_MALWARE_DEFAULT)
    1129                 :  , mCheckPhishing(CHECK_PHISHING_DEFAULT)
    1130               7 :  , mInUpdate(false)
    1131                 : {
    1132               7 : }
    1133                 : 
    1134              14 : nsUrlClassifierDBService::~nsUrlClassifierDBService()
    1135                 : {
    1136               7 :   sUrlClassifierDBService = nsnull;
    1137               7 : }
    1138                 : 
    1139                 : nsresult
    1140               7 : nsUrlClassifierDBService::Init()
    1141                 : {
    1142                 : #if defined(PR_LOGGING)
    1143               7 :   if (!gUrlClassifierDbServiceLog)
    1144               7 :     gUrlClassifierDbServiceLog = PR_NewLogModule("UrlClassifierDbService");
    1145                 : #endif
    1146                 : 
    1147                 :   nsresult rv;
    1148                 : 
    1149                 :   // Should we check document loads for malware URIs?
    1150              14 :   nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
    1151                 : 
    1152               7 :   PRInt32 gethashNoise = 0;
    1153               7 :   if (prefs) {
    1154                 :     bool tmpbool;
    1155               7 :     rv = prefs->GetBoolPref(CHECK_MALWARE_PREF, &tmpbool);
    1156               7 :     mCheckMalware = NS_SUCCEEDED(rv) ? tmpbool : CHECK_MALWARE_DEFAULT;
    1157                 : 
    1158               7 :     prefs->AddObserver(CHECK_MALWARE_PREF, this, false);
    1159                 : 
    1160               7 :     rv = prefs->GetBoolPref(CHECK_PHISHING_PREF, &tmpbool);
    1161               7 :     mCheckPhishing = NS_SUCCEEDED(rv) ? tmpbool : CHECK_PHISHING_DEFAULT;
    1162                 : 
    1163               7 :     prefs->AddObserver(CHECK_PHISHING_PREF, this, false);
    1164                 : 
    1165               7 :     if (NS_FAILED(prefs->GetIntPref(GETHASH_NOISE_PREF, &gethashNoise))) {
    1166               0 :       gethashNoise = GETHASH_NOISE_DEFAULT;
    1167                 :     }
    1168                 : 
    1169              14 :     nsXPIDLCString tmpstr;
    1170               7 :     if (NS_SUCCEEDED(prefs->GetCharPref(GETHASH_TABLES_PREF, getter_Copies(tmpstr)))) {
    1171               7 :       SplitTables(tmpstr, mGethashWhitelist);
    1172                 :     }
    1173                 : 
    1174               7 :     prefs->AddObserver(GETHASH_TABLES_PREF, this, false);
    1175                 : 
    1176                 :     PRInt32 tmpint;
    1177               7 :     rv = prefs->GetIntPref(CONFIRM_AGE_PREF, &tmpint);
    1178               7 :     PR_ATOMIC_SET(&gFreshnessGuarantee, NS_SUCCEEDED(rv) ? tmpint : CONFIRM_AGE_DEFAULT_SEC);
    1179                 : 
    1180               7 :     prefs->AddObserver(CONFIRM_AGE_PREF, this, false);
    1181                 :   }
    1182                 : 
    1183                 :   // Force PSM loading on main thread
    1184              14 :   nsCOMPtr<nsICryptoHash> acryptoHash = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
    1185               7 :   NS_ENSURE_SUCCESS(rv, rv);
    1186                 : 
    1187                 :   // Directory providers must also be accessed on the main thread.
    1188              14 :   nsCOMPtr<nsIFile> cacheDir;
    1189                 :   rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR,
    1190               7 :                               getter_AddRefs(cacheDir));
    1191               7 :   if (NS_FAILED(rv)) {
    1192                 :     rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
    1193               0 :                                 getter_AddRefs(cacheDir));
    1194                 :   }
    1195                 : 
    1196                 :   // Start the background thread.
    1197               7 :   rv = NS_NewThread(&gDbBackgroundThread);
    1198               7 :   if (NS_FAILED(rv))
    1199               0 :     return rv;
    1200                 : 
    1201               7 :   mWorker = new nsUrlClassifierDBServiceWorker();
    1202               7 :   if (!mWorker)
    1203               0 :     return NS_ERROR_OUT_OF_MEMORY;
    1204                 : 
    1205               7 :   rv = mWorker->Init(gethashNoise, cacheDir);
    1206               7 :   if (NS_FAILED(rv)) {
    1207               0 :     mWorker = nsnull;
    1208               0 :     return rv;
    1209                 :   }
    1210                 : 
    1211                 :   // Proxy for calling the worker on the background thread
    1212              14 :   mWorkerProxy = new UrlClassifierDBServiceWorkerProxy(mWorker);
    1213                 : 
    1214               7 :   mCompleters.Init();
    1215                 : 
    1216                 :   // Add an observer for shutdown
    1217                 :   nsCOMPtr<nsIObserverService> observerService =
    1218              14 :       mozilla::services::GetObserverService();
    1219               7 :   if (!observerService)
    1220               0 :     return NS_ERROR_FAILURE;
    1221                 : 
    1222               7 :   observerService->AddObserver(this, "profile-before-change", false);
    1223               7 :   observerService->AddObserver(this, "xpcom-shutdown-threads", false);
    1224                 : 
    1225               7 :   return NS_OK;
    1226                 : }
    1227                 : 
    1228                 : NS_IMETHODIMP
    1229               0 : nsUrlClassifierDBService::Classify(nsIURI *uri,
    1230                 :                                    nsIURIClassifierCallback* c,
    1231                 :                                    bool* result)
    1232                 : {
    1233               0 :   NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
    1234                 : 
    1235               0 :   if (!(mCheckMalware || mCheckPhishing)) {
    1236               0 :     *result = false;
    1237               0 :     return NS_OK;
    1238                 :   }
    1239                 : 
    1240                 :   nsRefPtr<nsUrlClassifierClassifyCallback> callback =
    1241               0 :     new nsUrlClassifierClassifyCallback(c, mCheckMalware, mCheckPhishing);
    1242               0 :   if (!callback) return NS_ERROR_OUT_OF_MEMORY;
    1243                 : 
    1244               0 :   nsresult rv = LookupURI(uri, callback, false, result);
    1245               0 :   if (rv == NS_ERROR_MALFORMED_URI) {
    1246               0 :     *result = false;
    1247                 :     // The URI had no hostname, don't try to classify it.
    1248               0 :     return NS_OK;
    1249                 :   }
    1250               0 :   NS_ENSURE_SUCCESS(rv, rv);
    1251                 : 
    1252               0 :   return NS_OK;
    1253                 : }
    1254                 : 
    1255                 : NS_IMETHODIMP
    1256             140 : nsUrlClassifierDBService::Lookup(const nsACString& spec,
    1257                 :                                  nsIUrlClassifierCallback* c)
    1258                 : {
    1259             140 :   NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
    1260                 : 
    1261             280 :   nsCOMPtr<nsIURI> uri;
    1262                 : 
    1263             140 :   nsresult rv = NS_NewURI(getter_AddRefs(uri), spec);
    1264             140 :   NS_ENSURE_SUCCESS(rv, rv);
    1265                 : 
    1266             140 :   uri = NS_GetInnermostURI(uri);
    1267             140 :   if (!uri) {
    1268               0 :     return NS_ERROR_FAILURE;
    1269                 :   }
    1270                 : 
    1271                 :   bool didLookup;
    1272             140 :   return LookupURI(uri, c, true, &didLookup);
    1273                 : }
    1274                 : 
    1275                 : nsresult
    1276             140 : nsUrlClassifierDBService::LookupURI(nsIURI* uri,
    1277                 :                                     nsIUrlClassifierCallback* c,
    1278                 :                                     bool forceLookup,
    1279                 :                                     bool *didLookup)
    1280                 : {
    1281             140 :   NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
    1282                 : 
    1283             280 :   nsCAutoString key;
    1284                 :   // Canonicalize the url
    1285                 :   nsCOMPtr<nsIUrlClassifierUtils> utilsService =
    1286             280 :     do_GetService(NS_URLCLASSIFIERUTILS_CONTRACTID);
    1287             140 :   nsresult rv = utilsService->GetKeyForURI(uri, key);
    1288             140 :   if (NS_FAILED(rv))
    1289               0 :     return rv;
    1290                 : 
    1291             140 :   if (forceLookup) {
    1292             140 :     *didLookup = true;
    1293                 :   } else {
    1294               0 :     bool clean = false;
    1295                 : 
    1296               0 :     if (!clean) {
    1297                 :       nsCOMPtr<nsIPermissionManager> permissionManager =
    1298               0 :         do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
    1299                 : 
    1300               0 :       if (permissionManager) {
    1301                 :         PRUint32 perm;
    1302               0 :         permissionManager->TestPermission(uri, "safe-browsing", &perm);
    1303               0 :         clean |= (perm == nsIPermissionManager::ALLOW_ACTION);
    1304                 :       }
    1305                 :     }
    1306                 : 
    1307               0 :     *didLookup = !clean;
    1308               0 :     if (clean) {
    1309               0 :       return NS_OK;
    1310                 :     }
    1311                 :   }
    1312                 : 
    1313                 :   // Create an nsUrlClassifierLookupCallback object.  This object will
    1314                 :   // take care of confirming partial hash matches if necessary before
    1315                 :   // calling the client's callback.
    1316                 :   nsCOMPtr<nsIUrlClassifierLookupCallback> callback =
    1317             280 :     new nsUrlClassifierLookupCallback(this, c);
    1318             140 :   if (!callback)
    1319               0 :     return NS_ERROR_OUT_OF_MEMORY;
    1320                 : 
    1321                 :   nsCOMPtr<nsIUrlClassifierLookupCallback> proxyCallback =
    1322             420 :     new UrlClassifierLookupCallbackProxy(callback);
    1323                 : 
    1324                 :   // Queue this lookup and call the lookup function to flush the queue if
    1325                 :   // necessary.
    1326             140 :   rv = mWorker->QueueLookup(key, proxyCallback);
    1327             140 :   NS_ENSURE_SUCCESS(rv, rv);
    1328                 : 
    1329             140 :   return mWorkerProxy->Lookup(EmptyCString(), nsnull);
    1330                 : }
    1331                 : 
    1332                 : NS_IMETHODIMP
    1333              48 : nsUrlClassifierDBService::GetTables(nsIUrlClassifierCallback* c)
    1334                 : {
    1335              48 :   NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
    1336                 : 
    1337                 :   // The proxy callback uses the current thread.
    1338                 :   nsCOMPtr<nsIUrlClassifierCallback> proxyCallback =
    1339              96 :     new UrlClassifierCallbackProxy(c);
    1340                 : 
    1341              48 :   return mWorkerProxy->GetTables(proxyCallback);
    1342                 : }
    1343                 : 
    1344                 : NS_IMETHODIMP
    1345              73 : nsUrlClassifierDBService::SetHashCompleter(const nsACString &tableName,
    1346                 :                                            nsIUrlClassifierHashCompleter *completer)
    1347                 : {
    1348              73 :   if (completer) {
    1349              27 :     if (!mCompleters.Put(tableName, completer)) {
    1350               0 :       return NS_ERROR_OUT_OF_MEMORY;
    1351                 :     }
    1352                 :   } else {
    1353              46 :     mCompleters.Remove(tableName);
    1354                 :   }
    1355                 : 
    1356              73 :   return NS_OK;
    1357                 : }
    1358                 : 
    1359                 : NS_IMETHODIMP
    1360              77 : nsUrlClassifierDBService::BeginUpdate(nsIUrlClassifierUpdateObserver *observer,
    1361                 :                                       const nsACString &updateTables,
    1362                 :                                       const nsACString &clientKey)
    1363                 : {
    1364              77 :   NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
    1365                 : 
    1366              77 :   if (mInUpdate)
    1367               0 :     return NS_ERROR_NOT_AVAILABLE;
    1368                 : 
    1369              77 :   mInUpdate = true;
    1370                 : 
    1371                 :   // The proxy observer uses the current thread
    1372                 :   nsCOMPtr<nsIUrlClassifierUpdateObserver> proxyObserver =
    1373             154 :     new UrlClassifierUpdateObserverProxy(observer);
    1374                 : 
    1375              77 :   return mWorkerProxy->BeginUpdate(proxyObserver, updateTables, clientKey);
    1376                 : }
    1377                 : 
    1378                 : NS_IMETHODIMP
    1379              89 : nsUrlClassifierDBService::BeginStream(const nsACString &table,
    1380                 :                                       const nsACString &serverMAC)
    1381                 : {
    1382              89 :   NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
    1383                 : 
    1384              89 :   return mWorkerProxy->BeginStream(table, serverMAC);
    1385                 : }
    1386                 : 
    1387                 : NS_IMETHODIMP
    1388              87 : nsUrlClassifierDBService::UpdateStream(const nsACString& aUpdateChunk)
    1389                 : {
    1390              87 :   NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
    1391                 : 
    1392              87 :   return mWorkerProxy->UpdateStream(aUpdateChunk);
    1393                 : }
    1394                 : 
    1395                 : NS_IMETHODIMP
    1396              87 : nsUrlClassifierDBService::FinishStream()
    1397                 : {
    1398              87 :   NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
    1399                 : 
    1400              87 :   return mWorkerProxy->FinishStream();
    1401                 : }
    1402                 : 
    1403                 : NS_IMETHODIMP
    1404              75 : nsUrlClassifierDBService::FinishUpdate()
    1405                 : {
    1406              75 :   NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
    1407                 : 
    1408              75 :   mInUpdate = false;
    1409                 : 
    1410              75 :   return mWorkerProxy->FinishUpdate();
    1411                 : }
    1412                 : 
    1413                 : 
    1414                 : NS_IMETHODIMP
    1415               2 : nsUrlClassifierDBService::CancelUpdate()
    1416                 : {
    1417               2 :   NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
    1418                 : 
    1419               2 :   mInUpdate = false;
    1420                 : 
    1421               2 :   return mWorkerProxy->CancelUpdate();
    1422                 : }
    1423                 : 
    1424                 : NS_IMETHODIMP
    1425              46 : nsUrlClassifierDBService::ResetDatabase()
    1426                 : {
    1427              46 :   NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
    1428                 : 
    1429              46 :   return mWorkerProxy->ResetDatabase();
    1430                 : }
    1431                 : 
    1432                 : nsresult
    1433              29 : nsUrlClassifierDBService::CacheCompletions(CacheResultArray *results)
    1434                 : {
    1435              29 :   NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
    1436                 : 
    1437              29 :   return mWorkerProxy->CacheCompletions(results);
    1438                 : }
    1439                 : 
    1440                 : nsresult
    1441             140 : nsUrlClassifierDBService::CacheMisses(PrefixArray *results)
    1442                 : {
    1443             140 :   NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
    1444                 : 
    1445             140 :   return mWorkerProxy->CacheMisses(results);
    1446                 : }
    1447                 : 
    1448                 : bool
    1449              54 : nsUrlClassifierDBService::GetCompleter(const nsACString &tableName,
    1450                 :                                        nsIUrlClassifierHashCompleter **completer)
    1451                 : {
    1452              54 :   if (mCompleters.Get(tableName, completer)) {
    1453              54 :     return true;
    1454                 :   }
    1455                 : 
    1456               0 :   if (!mGethashWhitelist.Contains(tableName)) {
    1457               0 :     return false;
    1458                 :   }
    1459                 : 
    1460               0 :   return NS_SUCCEEDED(CallGetService(NS_URLCLASSIFIERHASHCOMPLETER_CONTRACTID,
    1461                 :                                      completer));
    1462                 : }
    1463                 : 
    1464                 : NS_IMETHODIMP
    1465              18 : nsUrlClassifierDBService::Observe(nsISupports *aSubject, const char *aTopic,
    1466                 :                                   const PRUnichar *aData)
    1467                 : {
    1468              18 :   if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
    1469                 :     nsresult rv;
    1470               8 :     nsCOMPtr<nsIPrefBranch> prefs(do_QueryInterface(aSubject, &rv));
    1471               4 :     NS_ENSURE_SUCCESS(rv, rv);
    1472               4 :     if (NS_LITERAL_STRING(CHECK_MALWARE_PREF).Equals(aData)) {
    1473                 :       bool tmpbool;
    1474               0 :       rv = prefs->GetBoolPref(CHECK_MALWARE_PREF, &tmpbool);
    1475               0 :       mCheckMalware = NS_SUCCEEDED(rv) ? tmpbool : CHECK_MALWARE_DEFAULT;
    1476               4 :     } else if (NS_LITERAL_STRING(CHECK_PHISHING_PREF).Equals(aData)) {
    1477                 :       bool tmpbool;
    1478               0 :       rv = prefs->GetBoolPref(CHECK_PHISHING_PREF, &tmpbool);
    1479               0 :       mCheckPhishing = NS_SUCCEEDED(rv) ? tmpbool : CHECK_PHISHING_DEFAULT;
    1480               4 :     } else if (NS_LITERAL_STRING(GETHASH_TABLES_PREF).Equals(aData)) {
    1481               0 :       mGethashWhitelist.Clear();
    1482               0 :       nsXPIDLCString val;
    1483               0 :       if (NS_SUCCEEDED(prefs->GetCharPref(GETHASH_TABLES_PREF, getter_Copies(val)))) {
    1484               0 :         SplitTables(val, mGethashWhitelist);
    1485                 :       }
    1486               4 :     } else if (NS_LITERAL_STRING(CONFIRM_AGE_PREF).Equals(aData)) {
    1487                 :       PRInt32 tmpint;
    1488               4 :       rv = prefs->GetIntPref(CONFIRM_AGE_PREF, &tmpint);
    1489               4 :       PR_ATOMIC_SET(&gFreshnessGuarantee, NS_SUCCEEDED(rv) ? tmpint : CONFIRM_AGE_DEFAULT_SEC);
    1490                 :     }
    1491              21 :   } else if (!strcmp(aTopic, "profile-before-change") ||
    1492               7 :              !strcmp(aTopic, "xpcom-shutdown-threads")) {
    1493              14 :     Shutdown();
    1494                 :   } else {
    1495               0 :     return NS_ERROR_UNEXPECTED;
    1496                 :   }
    1497                 : 
    1498              18 :   return NS_OK;
    1499                 : }
    1500                 : 
    1501                 : // Join the background thread if it exists.
    1502                 : nsresult
    1503              14 : nsUrlClassifierDBService::Shutdown()
    1504                 : {
    1505              14 :   LOG(("shutting down db service\n"));
    1506                 : 
    1507              14 :   if (!gDbBackgroundThread)
    1508               7 :     return NS_OK;
    1509                 : 
    1510               7 :   mCompleters.Clear();
    1511                 : 
    1512              14 :   nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
    1513               7 :   if (prefs) {
    1514               7 :     prefs->RemoveObserver(CHECK_MALWARE_PREF, this);
    1515               7 :     prefs->RemoveObserver(CHECK_PHISHING_PREF, this);
    1516               7 :     prefs->RemoveObserver(GETHASH_TABLES_PREF, this);
    1517               7 :     prefs->RemoveObserver(CONFIRM_AGE_PREF, this);
    1518                 :   }
    1519                 : 
    1520                 :   nsresult rv;
    1521                 :   // First close the db connection.
    1522               7 :   if (mWorker) {
    1523               7 :     rv = mWorkerProxy->CancelUpdate();
    1524               7 :     NS_ASSERTION(NS_SUCCEEDED(rv), "failed to post cancel update event");
    1525                 : 
    1526               7 :     rv = mWorkerProxy->CloseDb();
    1527               7 :     NS_ASSERTION(NS_SUCCEEDED(rv), "failed to post close db event");
    1528                 :   }
    1529                 : 
    1530               7 :   mWorkerProxy = nsnull;
    1531                 : 
    1532               7 :   LOG(("joining background thread"));
    1533                 : 
    1534               7 :   gShuttingDownThread = true;
    1535                 : 
    1536               7 :   nsIThread *backgroundThread = gDbBackgroundThread;
    1537               7 :   gDbBackgroundThread = nsnull;
    1538               7 :   backgroundThread->Shutdown();
    1539               7 :   NS_RELEASE(backgroundThread);
    1540                 : 
    1541               7 :   return NS_OK;
    1542                 : }
    1543                 : 
    1544                 : nsIThread*
    1545             834 : nsUrlClassifierDBService::BackgroundThread()
    1546                 : {
    1547             834 :   return gDbBackgroundThread;
    1548                 : }
    1549                 : 

Generated by: LCOV version 1.7