LCOV - code coverage report
Current view: top level - toolkit/components/url-classifier - nsUrlClassifierStreamUpdater.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 207 459 45.1 %
Date: 2017-07-14 16:53:18 Functions: 16 24 66.7 %
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 "nsCRT.h"
       7             : #include "nsIHttpChannel.h"
       8             : #include "nsIObserverService.h"
       9             : #include "nsIStringStream.h"
      10             : #include "nsIUploadChannel.h"
      11             : #include "nsIURI.h"
      12             : #include "nsIUrlClassifierDBService.h"
      13             : #include "nsUrlClassifierUtils.h"
      14             : #include "nsNetUtil.h"
      15             : #include "nsStreamUtils.h"
      16             : #include "nsStringStream.h"
      17             : #include "nsToolkitCompsCID.h"
      18             : #include "nsUrlClassifierStreamUpdater.h"
      19             : #include "mozilla/BasePrincipal.h"
      20             : #include "mozilla/ErrorNames.h"
      21             : #include "mozilla/Logging.h"
      22             : #include "nsIInterfaceRequestor.h"
      23             : #include "mozilla/LoadContext.h"
      24             : #include "mozilla/Telemetry.h"
      25             : #include "nsContentUtils.h"
      26             : #include "nsIURLFormatter.h"
      27             : #include "Classifier.h"
      28             : 
      29             : using namespace mozilla::safebrowsing;
      30             : 
      31             : #define DEFAULT_RESPONSE_TIMEOUT_MS 15 * 1000
      32             : #define DEFAULT_TIMEOUT_MS 60 * 1000
      33             : static_assert(DEFAULT_TIMEOUT_MS > DEFAULT_RESPONSE_TIMEOUT_MS,
      34             :   "General timeout must be greater than reponse timeout");
      35             : 
      36             : static const char* gQuitApplicationMessage = "quit-application";
      37             : 
      38             : static uint32_t sResponseTimeoutMs = DEFAULT_RESPONSE_TIMEOUT_MS;
      39             : static uint32_t sTimeoutMs = DEFAULT_TIMEOUT_MS;
      40             : 
      41             : // Limit the list file size to 32mb
      42             : const uint32_t MAX_FILE_SIZE = (32 * 1024 * 1024);
      43             : 
      44             : // Retry delay when we failed to DownloadUpdate() if due to
      45             : // DBService busy.
      46             : const uint32_t FETCH_NEXT_REQUEST_RETRY_DELAY_MS = 1000;
      47             : 
      48             : #undef LOG
      49             : 
      50             : // MOZ_LOG=UrlClassifierStreamUpdater:5
      51             : static mozilla::LazyLogModule gUrlClassifierStreamUpdaterLog("UrlClassifierStreamUpdater");
      52             : #define LOG(args) TrimAndLog args
      53             : 
      54             : // Calls nsIURLFormatter::TrimSensitiveURLs to remove sensitive
      55             : // info from the logging message.
      56          14 : static MOZ_FORMAT_PRINTF(1, 2) void TrimAndLog(const char* aFmt, ...)
      57             : {
      58          28 :   nsString raw;
      59             : 
      60             :   va_list ap;
      61          14 :   va_start(ap, aFmt);
      62          14 :   raw.AppendPrintf(aFmt, ap);
      63          14 :   va_end(ap);
      64             : 
      65             :   nsCOMPtr<nsIURLFormatter> urlFormatter =
      66          28 :     do_GetService("@mozilla.org/toolkit/URLFormatterService;1");
      67             : 
      68          28 :   nsString trimmed;
      69          14 :   nsresult rv = urlFormatter->TrimSensitiveURLs(raw, trimmed);
      70          14 :   if (NS_FAILED(rv)) {
      71           0 :     trimmed = EmptyString();
      72             :   }
      73             : 
      74             :   // Use %s so we aren't exposing random strings to printf interpolation.
      75          14 :   MOZ_LOG(gUrlClassifierStreamUpdaterLog,
      76             :           mozilla::LogLevel::Debug,
      77             :           ("%s", NS_ConvertUTF16toUTF8(trimmed).get()));
      78          14 : }
      79             : 
      80             : // This class does absolutely nothing, except pass requests onto the DBService.
      81             : 
      82             : ///////////////////////////////////////////////////////////////////////////////
      83             : // nsIUrlClassiferStreamUpdater implementation
      84             : // Handles creating/running the stream listener
      85             : 
      86           1 : nsUrlClassifierStreamUpdater::nsUrlClassifierStreamUpdater()
      87             :   : mIsUpdating(false), mInitialized(false), mDownloadError(false),
      88           1 :     mBeganStream(false), mChannel(nullptr), mTelemetryClockStart(0)
      89             : {
      90           1 :   LOG(("nsUrlClassifierStreamUpdater init [this=%p]", this));
      91           1 : }
      92             : 
      93         103 : NS_IMPL_ISUPPORTS(nsUrlClassifierStreamUpdater,
      94             :                   nsIUrlClassifierStreamUpdater,
      95             :                   nsIUrlClassifierUpdateObserver,
      96             :                   nsIRequestObserver,
      97             :                   nsIStreamListener,
      98             :                   nsIObserver,
      99             :                   nsIInterfaceRequestor,
     100             :                   nsITimerCallback)
     101             : 
     102             : /**
     103             :  * Clear out the update.
     104             :  */
     105             : void
     106           1 : nsUrlClassifierStreamUpdater::DownloadDone()
     107             : {
     108           1 :   LOG(("nsUrlClassifierStreamUpdater::DownloadDone [this=%p]", this));
     109           1 :   mIsUpdating = false;
     110             : 
     111           1 :   mPendingUpdates.Clear();
     112           1 :   mDownloadError = false;
     113           1 :   mSuccessCallback = nullptr;
     114           1 :   mUpdateErrorCallback = nullptr;
     115           1 :   mDownloadErrorCallback = nullptr;
     116           1 : }
     117             : 
     118             : ///////////////////////////////////////////////////////////////////////////////
     119             : // nsIUrlClassifierStreamUpdater implementation
     120             : 
     121             : nsresult
     122           1 : nsUrlClassifierStreamUpdater::FetchUpdate(nsIURI *aUpdateUrl,
     123             :                                           const nsACString & aRequestPayload,
     124             :                                           bool aIsPostRequest,
     125             :                                           const nsACString & aStreamTable)
     126             : {
     127             : 
     128             : #ifdef DEBUG
     129           2 :   LOG(("Fetching update %s from %s",
     130           1 :        aRequestPayload.Data(), aUpdateUrl->GetSpecOrDefault().get()));
     131             : #endif
     132             : 
     133             :   nsresult rv;
     134             :   uint32_t loadFlags = nsIChannel::INHIBIT_CACHING |
     135           1 :                        nsIChannel::LOAD_BYPASS_CACHE;
     136           1 :   rv = NS_NewChannel(getter_AddRefs(mChannel),
     137             :                      aUpdateUrl,
     138             :                      nsContentUtils::GetSystemPrincipal(),
     139             :                      nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
     140             :                      nsIContentPolicy::TYPE_OTHER,
     141             :                      nullptr,  // aLoadGroup
     142             :                      this,     // aInterfaceRequestor
     143             :                      loadFlags);
     144             : 
     145           1 :   NS_ENSURE_SUCCESS(rv, rv);
     146             : 
     147           2 :   nsCOMPtr<nsILoadInfo> loadInfo = mChannel->GetLoadInfo();
     148           2 :   mozilla::OriginAttributes attrs;
     149           1 :   attrs.mFirstPartyDomain.AssignLiteral(NECKO_SAFEBROWSING_FIRST_PARTY_DOMAIN);
     150           1 :   if (loadInfo) {
     151           1 :     loadInfo->SetOriginAttributes(attrs);
     152             :   }
     153             : 
     154           1 :   mBeganStream = false;
     155             : 
     156           1 :   if (!aIsPostRequest) {
     157             :     // We use POST method to send our request in v2. In v4, the request
     158             :     // needs to be embedded to the URL and use GET method to send.
     159             :     // However, from the Chromium source code, a extended HTTP header has
     160             :     // to be sent along with the request to make the request succeed.
     161             :     // The following description is from Chromium source code:
     162             :     //
     163             :     // "The following header informs the envelope server (which sits in
     164             :     // front of Google's stubby server) that the received GET request should be
     165             :     // interpreted as a POST."
     166             :     //
     167           0 :     nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel, &rv);
     168           0 :     NS_ENSURE_SUCCESS(rv, rv);
     169             : 
     170           0 :     rv = httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("X-HTTP-Method-Override"),
     171           0 :                                        NS_LITERAL_CSTRING("POST"),
     172           0 :                                        false);
     173           0 :     NS_ENSURE_SUCCESS(rv, rv);
     174           1 :   } else if (!aRequestPayload.IsEmpty()) {
     175           1 :     rv = AddRequestBody(aRequestPayload);
     176           1 :     NS_ENSURE_SUCCESS(rv, rv);
     177             :   }
     178             : 
     179             :   // Set the appropriate content type for file/data URIs, for unit testing
     180             :   // purposes.
     181             :   // This is only used for testing and should be deleted.
     182             :   bool match;
     183           2 :   if ((NS_SUCCEEDED(aUpdateUrl->SchemeIs("file", &match)) && match) ||
     184           2 :       (NS_SUCCEEDED(aUpdateUrl->SchemeIs("data", &match)) && match)) {
     185           0 :     mChannel->SetContentType(NS_LITERAL_CSTRING("application/vnd.google.safebrowsing-update"));
     186             :   } else {
     187             :     // We assume everything else is an HTTP request.
     188             : 
     189             :     // Disable keepalive.
     190           2 :     nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel, &rv);
     191           1 :     NS_ENSURE_SUCCESS(rv, rv);
     192           1 :     rv = httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Connection"), NS_LITERAL_CSTRING("close"), false);
     193           1 :     NS_ENSURE_SUCCESS(rv, rv);
     194             :   }
     195             : 
     196             :   // Make the request.
     197           1 :   rv = mChannel->AsyncOpen2(this);
     198           1 :   NS_ENSURE_SUCCESS(rv, rv);
     199             : 
     200           1 :   mTelemetryClockStart = PR_IntervalNow();
     201           1 :   mStreamTable = aStreamTable;
     202             : 
     203             :   static bool preferencesInitialized = false;
     204             : 
     205           1 :   if (!preferencesInitialized) {
     206             :     mozilla::Preferences::AddUintVarCache(&sTimeoutMs,
     207             :                                           "urlclassifier.update.timeout_ms",
     208           1 :                                           DEFAULT_TIMEOUT_MS);
     209             :     mozilla::Preferences::AddUintVarCache(&sResponseTimeoutMs,
     210             :                                           "urlclassifier.update.response_timeout_ms",
     211           1 :                                           DEFAULT_RESPONSE_TIMEOUT_MS);
     212           1 :     preferencesInitialized = true;
     213             :   }
     214             : 
     215           1 :   if (sResponseTimeoutMs > sTimeoutMs) {
     216             :     NS_WARNING("Safe Browsing response timeout is greater than the general "
     217           0 :       "timeout. Disabling these update timeouts.");
     218           0 :     return NS_OK;
     219             :   }
     220           1 :   mResponseTimeoutTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
     221           1 :   if (NS_SUCCEEDED(rv)) {
     222           2 :     rv = mResponseTimeoutTimer->InitWithCallback(this,
     223             :                                                  sResponseTimeoutMs,
     224           1 :                                                  nsITimer::TYPE_ONE_SHOT);
     225             :   }
     226             : 
     227           1 :   mTimeoutTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
     228             : 
     229           1 :   if (NS_SUCCEEDED(rv)) {
     230           1 :     if (sTimeoutMs < DEFAULT_TIMEOUT_MS) {
     231             :       LOG(("Download update timeout %d ms (< %d ms) would be too small",
     232           0 :            sTimeoutMs, DEFAULT_TIMEOUT_MS));
     233             :     }
     234           2 :     rv = mTimeoutTimer->InitWithCallback(this,
     235             :                                          sTimeoutMs,
     236           1 :                                          nsITimer::TYPE_ONE_SHOT);
     237             :   }
     238             : 
     239           1 :   return NS_OK;
     240             : }
     241             : 
     242             : nsresult
     243           1 : nsUrlClassifierStreamUpdater::FetchUpdate(const nsACString & aUpdateUrl,
     244             :                                           const nsACString & aRequestPayload,
     245             :                                           bool aIsPostRequest,
     246             :                                           const nsACString & aStreamTable)
     247             : {
     248           1 :   LOG(("(pre) Fetching update from %s\n", PromiseFlatCString(aUpdateUrl).get()));
     249             : 
     250           2 :   nsCString updateUrl(aUpdateUrl);
     251           1 :   if (!aIsPostRequest) {
     252           0 :     updateUrl.AppendPrintf("&$req=%s", nsCString(aRequestPayload).get());
     253             :   }
     254             : 
     255           2 :   nsCOMPtr<nsIURI> uri;
     256           1 :   nsresult rv = NS_NewURI(getter_AddRefs(uri), updateUrl);
     257           1 :   NS_ENSURE_SUCCESS(rv, rv);
     258             : 
     259           2 :   nsAutoCString urlSpec;
     260           1 :   uri->GetAsciiSpec(urlSpec);
     261             : 
     262           1 :   LOG(("(post) Fetching update from %s\n", urlSpec.get()));
     263             : 
     264           1 :   return FetchUpdate(uri, aRequestPayload, aIsPostRequest, aStreamTable);
     265             : }
     266             : 
     267             : NS_IMETHODIMP
     268           1 : nsUrlClassifierStreamUpdater::DownloadUpdates(
     269             :   const nsACString &aRequestTables,
     270             :   const nsACString &aRequestPayload,
     271             :   bool aIsPostRequest,
     272             :   const nsACString &aUpdateUrl,
     273             :   nsIUrlClassifierCallback *aSuccessCallback,
     274             :   nsIUrlClassifierCallback *aUpdateErrorCallback,
     275             :   nsIUrlClassifierCallback *aDownloadErrorCallback,
     276             :   bool *_retval)
     277             : {
     278           1 :   NS_ENSURE_ARG(aSuccessCallback);
     279           1 :   NS_ENSURE_ARG(aUpdateErrorCallback);
     280           1 :   NS_ENSURE_ARG(aDownloadErrorCallback);
     281             : 
     282           1 :   if (mIsUpdating) {
     283           0 :     LOG(("Already updating, queueing update %s from %s", aRequestPayload.Data(),
     284           0 :          aUpdateUrl.Data()));
     285           0 :     *_retval = false;
     286           0 :     PendingRequest *request = mPendingRequests.AppendElement();
     287           0 :     request->mTables = aRequestTables;
     288           0 :     request->mRequestPayload = aRequestPayload;
     289           0 :     request->mIsPostRequest = aIsPostRequest;
     290           0 :     request->mUrl = aUpdateUrl;
     291           0 :     request->mSuccessCallback = aSuccessCallback;
     292           0 :     request->mUpdateErrorCallback = aUpdateErrorCallback;
     293           0 :     request->mDownloadErrorCallback = aDownloadErrorCallback;
     294           0 :     return NS_OK;
     295             :   }
     296             : 
     297           1 :   if (aUpdateUrl.IsEmpty()) {
     298           0 :     NS_ERROR("updateUrl not set");
     299           0 :     return NS_ERROR_NOT_INITIALIZED;
     300             :   }
     301             : 
     302             :   nsresult rv;
     303             : 
     304           1 :   if (!mInitialized) {
     305             :     // Add an observer for shutdown so we can cancel any pending list
     306             :     // downloads.  quit-application is the same event that the download
     307             :     // manager listens for and uses to cancel pending downloads.
     308             :     nsCOMPtr<nsIObserverService> observerService =
     309           2 :       mozilla::services::GetObserverService();
     310           1 :     if (!observerService)
     311           0 :       return NS_ERROR_FAILURE;
     312             : 
     313           1 :     observerService->AddObserver(this, gQuitApplicationMessage, false);
     314             : 
     315           1 :     mDBService = do_GetService(NS_URLCLASSIFIERDBSERVICE_CONTRACTID, &rv);
     316           1 :     NS_ENSURE_SUCCESS(rv, rv);
     317             : 
     318           1 :     mInitialized = true;
     319             :   }
     320             : 
     321           1 :   rv = mDBService->BeginUpdate(this, aRequestTables);
     322           1 :   if (rv == NS_ERROR_NOT_AVAILABLE) {
     323           0 :     LOG(("Service busy, already updating, queuing update %s from %s",
     324           0 :          aRequestPayload.Data(), aUpdateUrl.Data()));
     325           0 :     *_retval = false;
     326           0 :     PendingRequest *request = mPendingRequests.AppendElement();
     327           0 :     request->mTables = aRequestTables;
     328           0 :     request->mRequestPayload = aRequestPayload;
     329           0 :     request->mIsPostRequest = aIsPostRequest;
     330           0 :     request->mUrl = aUpdateUrl;
     331           0 :     request->mSuccessCallback = aSuccessCallback;
     332           0 :     request->mUpdateErrorCallback = aUpdateErrorCallback;
     333           0 :     request->mDownloadErrorCallback = aDownloadErrorCallback;
     334             : 
     335             :     // We cannot guarantee that we will be notified when DBService is done
     336             :     // processing the current update, so we fire a retry timer on our own.
     337             :     nsresult rv;
     338           0 :     mFetchNextRequestTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
     339           0 :     if (NS_SUCCEEDED(rv)) {
     340           0 :       rv = mFetchNextRequestTimer->InitWithCallback(this,
     341             :                                                     FETCH_NEXT_REQUEST_RETRY_DELAY_MS,
     342           0 :                                                     nsITimer::TYPE_ONE_SHOT);
     343             :     }
     344             : 
     345           0 :     return NS_OK;
     346             :   }
     347             : 
     348           1 :   if (NS_FAILED(rv)) {
     349           0 :     return rv;
     350             :   }
     351             : 
     352             :   nsCOMPtr<nsIUrlClassifierUtils> urlUtil =
     353           2 :     do_GetService(NS_URLCLASSIFIERUTILS_CONTRACTID);
     354             : 
     355           2 :   nsTArray<nsCString> tables;
     356           1 :   mozilla::safebrowsing::Classifier::SplitTables(aRequestTables, tables);
     357           2 :   urlUtil->GetTelemetryProvider(tables.SafeElementAt(0, EmptyCString()),
     358           2 :                                 mTelemetryProvider);
     359             : 
     360           1 :   mSuccessCallback = aSuccessCallback;
     361           1 :   mUpdateErrorCallback = aUpdateErrorCallback;
     362           1 :   mDownloadErrorCallback = aDownloadErrorCallback;
     363             : 
     364           1 :   mIsUpdating = true;
     365           1 :   *_retval = true;
     366             : 
     367           1 :   LOG(("FetchUpdate: %s", aUpdateUrl.Data()));
     368             : 
     369           1 :   return FetchUpdate(aUpdateUrl, aRequestPayload, aIsPostRequest, EmptyCString());
     370             : }
     371             : 
     372             : ///////////////////////////////////////////////////////////////////////////////
     373             : // nsIUrlClassifierUpdateObserver implementation
     374             : 
     375             : NS_IMETHODIMP
     376           0 : nsUrlClassifierStreamUpdater::UpdateUrlRequested(const nsACString &aUrl,
     377             :                                                  const nsACString &aTable)
     378             : {
     379           0 :   LOG(("Queuing requested update from %s\n", PromiseFlatCString(aUrl).get()));
     380             : 
     381           0 :   PendingUpdate *update = mPendingUpdates.AppendElement();
     382           0 :   if (!update)
     383           0 :     return NS_ERROR_OUT_OF_MEMORY;
     384             : 
     385             :   // Allow data: and file: urls for unit testing purposes, otherwise assume http
     386           0 :   if (StringBeginsWith(aUrl, NS_LITERAL_CSTRING("data:")) ||
     387           0 :       StringBeginsWith(aUrl, NS_LITERAL_CSTRING("file:"))) {
     388           0 :     update->mUrl = aUrl;
     389             :   } else {
     390             :     // For unittesting update urls to localhost should use http, not https
     391             :     // (otherwise the connection will fail silently, since there will be no
     392             :     // cert available).
     393           0 :     if (!StringBeginsWith(aUrl, NS_LITERAL_CSTRING("localhost"))) {
     394           0 :       update->mUrl = NS_LITERAL_CSTRING("https://") + aUrl;
     395             :     } else {
     396           0 :       update->mUrl = NS_LITERAL_CSTRING("http://") + aUrl;
     397             :     }
     398             :   }
     399           0 :   update->mTable = aTable;
     400             : 
     401           0 :   return NS_OK;
     402             : }
     403             : 
     404             : nsresult
     405           0 : nsUrlClassifierStreamUpdater::FetchNext()
     406             : {
     407           0 :   if (mPendingUpdates.Length() == 0) {
     408           0 :     return NS_OK;
     409             :   }
     410             : 
     411           0 :   PendingUpdate &update = mPendingUpdates[0];
     412           0 :   LOG(("Fetching update url: %s\n", update.mUrl.get()));
     413           0 :   nsresult rv = FetchUpdate(update.mUrl,
     414           0 :                             EmptyCString(),
     415             :                             true, // This method is for v2 and v2 is always a POST.
     416           0 :                             update.mTable);
     417           0 :   if (NS_FAILED(rv)) {
     418           0 :     LOG(("Error fetching update url: %s\n", update.mUrl.get()));
     419             :     // We can commit the urls that we've applied so far.  This is
     420             :     // probably a transient server problem, so trigger backoff.
     421           0 :     mDownloadError = true;
     422           0 :     mDBService->FinishUpdate();
     423           0 :     return rv;
     424             :   }
     425             : 
     426           0 :   mPendingUpdates.RemoveElementAt(0);
     427             : 
     428           0 :   return NS_OK;
     429             : }
     430             : 
     431             : nsresult
     432           1 : nsUrlClassifierStreamUpdater::FetchNextRequest()
     433             : {
     434           1 :   if (mPendingRequests.Length() == 0) {
     435           1 :     LOG(("No more requests, returning"));
     436           1 :     return NS_OK;
     437             :   }
     438             : 
     439           0 :   PendingRequest request = mPendingRequests[0];
     440           0 :   mPendingRequests.RemoveElementAt(0);
     441           0 :   LOG(("Stream updater: fetching next request: %s, %s",
     442           0 :        request.mTables.get(), request.mUrl.get()));
     443             :   bool dummy;
     444           0 :   DownloadUpdates(
     445             :     request.mTables,
     446             :     request.mRequestPayload,
     447           0 :     request.mIsPostRequest,
     448             :     request.mUrl,
     449             :     request.mSuccessCallback,
     450             :     request.mUpdateErrorCallback,
     451             :     request.mDownloadErrorCallback,
     452           0 :     &dummy);
     453           0 :   return NS_OK;
     454             : }
     455             : 
     456             : NS_IMETHODIMP
     457           0 : nsUrlClassifierStreamUpdater::StreamFinished(nsresult status,
     458             :                                              uint32_t requestedDelay)
     459             : {
     460             :   // We are a service and may not be reset with Init between calls, so reset
     461             :   // mBeganStream manually.
     462           0 :   mBeganStream = false;
     463             :   LOG(("nsUrlClassifierStreamUpdater::StreamFinished [%" PRIx32 ", %d]",
     464           0 :        static_cast<uint32_t>(status), requestedDelay));
     465           0 :   if (NS_FAILED(status) || mPendingUpdates.Length() == 0) {
     466             :     // We're done.
     467           0 :     LOG(("nsUrlClassifierStreamUpdater::Done [this=%p]", this));
     468           0 :     mDBService->FinishUpdate();
     469           0 :     return NS_OK;
     470             :   }
     471             : 
     472             :   // This timer is for fetching indirect updates ("forwards") from any "u:" lines
     473             :   // that we encountered while processing the server response. It is NOT for
     474             :   // scheduling the next time we pull the list from the server. That's a different
     475             :   // timer in listmanager.js (see bug 1110891).
     476             :   nsresult rv;
     477           0 :   mFetchIndirectUpdatesTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
     478           0 :   if (NS_SUCCEEDED(rv)) {
     479           0 :     rv = mFetchIndirectUpdatesTimer->InitWithCallback(this, requestedDelay,
     480           0 :                                                       nsITimer::TYPE_ONE_SHOT);
     481             :   }
     482             : 
     483           0 :   if (NS_FAILED(rv)) {
     484           0 :     NS_WARNING("Unable to initialize timer, fetching next safebrowsing item immediately");
     485           0 :     return FetchNext();
     486             :   }
     487             : 
     488           0 :   return NS_OK;
     489             : }
     490             : 
     491             : NS_IMETHODIMP
     492           1 : nsUrlClassifierStreamUpdater::UpdateSuccess(uint32_t requestedTimeout)
     493             : {
     494           1 :   LOG(("nsUrlClassifierStreamUpdater::UpdateSuccess [this=%p]", this));
     495           1 :   if (mPendingUpdates.Length() != 0) {
     496           0 :     NS_WARNING("Didn't fetch all safebrowsing update redirects");
     497             :   }
     498             : 
     499             :   // DownloadDone() clears mSuccessCallback, so we save it off here.
     500           2 :   nsCOMPtr<nsIUrlClassifierCallback> successCallback = mDownloadError ? nullptr : mSuccessCallback.get();
     501           2 :   nsCOMPtr<nsIUrlClassifierCallback> downloadErrorCallback = mDownloadError ? mDownloadErrorCallback.get() : nullptr;
     502           1 :   DownloadDone();
     503             : 
     504           2 :   nsAutoCString strTimeout;
     505           1 :   strTimeout.AppendInt(requestedTimeout);
     506           1 :   if (successCallback) {
     507             :     LOG(("nsUrlClassifierStreamUpdater::UpdateSuccess callback [this=%p]",
     508           0 :          this));
     509           0 :     successCallback->HandleEvent(strTimeout);
     510           1 :   } else if (downloadErrorCallback) {
     511           1 :     downloadErrorCallback->HandleEvent(mDownloadErrorStatusStr);
     512           1 :     mDownloadErrorStatusStr = EmptyCString();
     513           1 :     LOG(("Notify download error callback in UpdateSuccess [this=%p]", this));
     514             :   }
     515             :   // Now fetch the next request
     516           1 :   LOG(("stream updater: calling into fetch next request"));
     517           1 :   FetchNextRequest();
     518             : 
     519           2 :   return NS_OK;
     520             : }
     521             : 
     522             : NS_IMETHODIMP
     523           0 : nsUrlClassifierStreamUpdater::UpdateError(nsresult result)
     524             : {
     525           0 :   LOG(("nsUrlClassifierStreamUpdater::UpdateError [this=%p]", this));
     526             : 
     527             :   // DownloadDone() clears mUpdateErrorCallback, so we save it off here.
     528           0 :   nsCOMPtr<nsIUrlClassifierCallback> errorCallback = mDownloadError ? nullptr : mUpdateErrorCallback.get();
     529           0 :   nsCOMPtr<nsIUrlClassifierCallback> downloadErrorCallback = mDownloadError ? mDownloadErrorCallback.get() : nullptr;
     530           0 :   DownloadDone();
     531             : 
     532           0 :   nsAutoCString strResult;
     533           0 :   strResult.AppendInt(static_cast<uint32_t>(result));
     534           0 :   if (errorCallback) {
     535           0 :     errorCallback->HandleEvent(strResult);
     536           0 :   } else if (downloadErrorCallback) {
     537           0 :     LOG(("Notify download error callback in UpdateError [this=%p]", this));
     538           0 :     downloadErrorCallback->HandleEvent(mDownloadErrorStatusStr);
     539           0 :     mDownloadErrorStatusStr = EmptyCString();
     540             :   }
     541             : 
     542           0 :   return NS_OK;
     543             : }
     544             : 
     545             : nsresult
     546           1 : nsUrlClassifierStreamUpdater::AddRequestBody(const nsACString &aRequestBody)
     547             : {
     548             :   nsresult rv;
     549             :   nsCOMPtr<nsIStringInputStream> strStream =
     550           2 :     do_CreateInstance(NS_STRINGINPUTSTREAM_CONTRACTID, &rv);
     551           1 :   NS_ENSURE_SUCCESS(rv, rv);
     552             : 
     553           2 :   rv = strStream->SetData(aRequestBody.BeginReading(),
     554           1 :                           aRequestBody.Length());
     555           1 :   NS_ENSURE_SUCCESS(rv, rv);
     556             : 
     557           2 :   nsCOMPtr<nsIUploadChannel> uploadChannel = do_QueryInterface(mChannel, &rv);
     558           1 :   NS_ENSURE_SUCCESS(rv, rv);
     559             : 
     560           3 :   rv = uploadChannel->SetUploadStream(strStream,
     561           2 :                                       NS_LITERAL_CSTRING("text/plain"),
     562           3 :                                       -1);
     563           1 :   NS_ENSURE_SUCCESS(rv, rv);
     564             : 
     565           2 :   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel, &rv);
     566           1 :   NS_ENSURE_SUCCESS(rv, rv);
     567             : 
     568           1 :   rv = httpChannel->SetRequestMethod(NS_LITERAL_CSTRING("POST"));
     569           1 :   NS_ENSURE_SUCCESS(rv, rv);
     570             : 
     571           1 :   return NS_OK;
     572             : }
     573             : 
     574             : // We might need to expand the bucket here if telemetry shows lots of errors
     575             : // are neither connection errors nor DNS errors.
     576           0 : static uint8_t NetworkErrorToBucket(nsresult rv)
     577             : {
     578           0 :   switch(rv) {
     579             :   // Connection errors
     580           0 :   case NS_ERROR_ALREADY_CONNECTED:        return 2;
     581           0 :   case NS_ERROR_NOT_CONNECTED:            return 3;
     582           0 :   case NS_ERROR_CONNECTION_REFUSED:       return 4;
     583           0 :   case NS_ERROR_NET_TIMEOUT:              return 5;
     584           0 :   case NS_ERROR_OFFLINE:                  return 6;
     585           0 :   case NS_ERROR_PORT_ACCESS_NOT_ALLOWED:  return 7;
     586           0 :   case NS_ERROR_NET_RESET:                return 8;
     587           0 :   case NS_ERROR_NET_INTERRUPT:            return 9;
     588           0 :   case NS_ERROR_PROXY_CONNECTION_REFUSED: return 10;
     589           0 :   case NS_ERROR_NET_PARTIAL_TRANSFER:     return 11;
     590           0 :   case NS_ERROR_NET_INADEQUATE_SECURITY:  return 12;
     591             :   // DNS errors
     592           0 :   case NS_ERROR_UNKNOWN_HOST:             return 13;
     593           0 :   case NS_ERROR_DNS_LOOKUP_QUEUE_FULL:    return 14;
     594           0 :   case NS_ERROR_UNKNOWN_PROXY_HOST:       return 15;
     595             :   // Others
     596           0 :   default:                                return 1;
     597             :   }
     598             : }
     599             : 
     600             : // Map the HTTP response code to a Telemetry bucket
     601           1 : static uint32_t HTTPStatusToBucket(uint32_t status)
     602             : {
     603             :   uint32_t statusBucket;
     604           1 :   switch (status) {
     605             :   case 100:
     606             :   case 101:
     607             :     // Unexpected 1xx return code
     608           0 :     statusBucket = 0;
     609           0 :     break;
     610             :   case 200:
     611             :     // OK - Data is available in the HTTP response body.
     612           0 :     statusBucket = 1;
     613           0 :     break;
     614             :   case 201:
     615             :   case 202:
     616             :   case 203:
     617             :   case 205:
     618             :   case 206:
     619             :     // Unexpected 2xx return code
     620           0 :     statusBucket = 2;
     621           0 :     break;
     622             :   case 204:
     623             :     // No Content
     624           0 :     statusBucket = 3;
     625           0 :     break;
     626             :   case 300:
     627             :   case 301:
     628             :   case 302:
     629             :   case 303:
     630             :   case 304:
     631             :   case 305:
     632             :   case 307:
     633             :   case 308:
     634             :     // Unexpected 3xx return code
     635           0 :     statusBucket = 4;
     636           0 :     break;
     637             :   case 400:
     638             :     // Bad Request - The HTTP request was not correctly formed.
     639             :     // The client did not provide all required CGI parameters.
     640           0 :     statusBucket = 5;
     641           0 :     break;
     642             :   case 401:
     643             :   case 402:
     644             :   case 405:
     645             :   case 406:
     646             :   case 407:
     647             :   case 409:
     648             :   case 410:
     649             :   case 411:
     650             :   case 412:
     651             :   case 414:
     652             :   case 415:
     653             :   case 416:
     654             :   case 417:
     655             :   case 421:
     656             :   case 426:
     657             :   case 428:
     658             :   case 429:
     659             :   case 431:
     660             :   case 451:
     661             :     // Unexpected 4xx return code
     662           0 :     statusBucket = 6;
     663           0 :     break;
     664             :   case 403:
     665             :     // Forbidden - The client id is invalid.
     666           0 :     statusBucket = 7;
     667           0 :     break;
     668             :   case 404:
     669             :     // Not Found
     670           1 :     statusBucket = 8;
     671           1 :     break;
     672             :   case 408:
     673             :     // Request Timeout
     674           0 :     statusBucket = 9;
     675           0 :     break;
     676             :   case 413:
     677             :     // Request Entity Too Large - Bug 1150334
     678           0 :     statusBucket = 10;
     679           0 :     break;
     680             :   case 500:
     681             :   case 501:
     682             :   case 510:
     683             :     // Unexpected 5xx return code
     684           0 :     statusBucket = 11;
     685           0 :     break;
     686             :   case 502:
     687             :   case 504:
     688             :   case 511:
     689             :     // Local network errors, we'll ignore these.
     690           0 :     statusBucket = 12;
     691           0 :     break;
     692             :   case 503:
     693             :     // Service Unavailable - The server cannot handle the request.
     694             :     // Clients MUST follow the backoff behavior specified in the
     695             :     // Request Frequency section.
     696           0 :     statusBucket = 13;
     697           0 :     break;
     698             :   case 505:
     699             :     // HTTP Version Not Supported - The server CANNOT handle the requested
     700             :     // protocol major version.
     701           0 :     statusBucket = 14;
     702           0 :     break;
     703             :   default:
     704           0 :     statusBucket = 15;
     705             :   };
     706           1 :   return statusBucket;
     707             : }
     708             : 
     709             : ///////////////////////////////////////////////////////////////////////////////
     710             : // nsIStreamListenerObserver implementation
     711             : 
     712             : NS_IMETHODIMP
     713           1 : nsUrlClassifierStreamUpdater::OnStartRequest(nsIRequest *request,
     714             :                                              nsISupports* context)
     715             : {
     716             :   nsresult rv;
     717           1 :   bool downloadError = false;
     718           2 :   nsAutoCString strStatus;
     719           1 :   nsresult status = NS_OK;
     720             : 
     721             :   // Only update if we got http success header
     722           2 :   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(request);
     723           1 :   if (httpChannel) {
     724           1 :     rv = httpChannel->GetStatus(&status);
     725           1 :     NS_ENSURE_SUCCESS(rv, rv);
     726             : 
     727           1 :     if (MOZ_LOG_TEST(gUrlClassifierStreamUpdaterLog, mozilla::LogLevel::Debug)) {
     728           0 :       nsAutoCString errorName, spec;
     729           0 :       mozilla::GetErrorName(status, errorName);
     730           0 :       nsCOMPtr<nsIURI> uri;
     731           0 :       rv = httpChannel->GetURI(getter_AddRefs(uri));
     732           0 :       if (NS_SUCCEEDED(rv) && uri) {
     733           0 :         uri->GetAsciiSpec(spec);
     734             :       }
     735           0 :       LOG(("nsUrlClassifierStreamUpdater::OnStartRequest "
     736             :            "(status=%s, uri=%s, this=%p)", errorName.get(),
     737           0 :            spec.get(), this));
     738             :     }
     739           1 :     if (mTelemetryClockStart > 0) {
     740           1 :       uint32_t msecs = PR_IntervalToMilliseconds(PR_IntervalNow() - mTelemetryClockStart);
     741           1 :       mozilla::Telemetry::Accumulate(mozilla::Telemetry::URLCLASSIFIER_UPDATE_SERVER_RESPONSE_TIME,
     742           1 :                                      mTelemetryProvider, msecs);
     743             : 
     744             :     }
     745             : 
     746           1 :     if (mResponseTimeoutTimer) {
     747           1 :       mResponseTimeoutTimer->Cancel();
     748           1 :       mResponseTimeoutTimer = nullptr;
     749             :     }
     750             : 
     751           1 :     uint8_t netErrCode = NS_FAILED(status) ? NetworkErrorToBucket(status) : 0;
     752           1 :     mozilla::Telemetry::Accumulate(
     753             :       mozilla::Telemetry::URLCLASSIFIER_UPDATE_REMOTE_NETWORK_ERROR,
     754           1 :       mTelemetryProvider, netErrCode);
     755             : 
     756           1 :     if (NS_FAILED(status)) {
     757             :       // Assume we're overloading the server and trigger backoff.
     758           0 :       downloadError = true;
     759             :     } else {
     760           1 :       bool succeeded = false;
     761           1 :       rv = httpChannel->GetRequestSucceeded(&succeeded);
     762           1 :       NS_ENSURE_SUCCESS(rv, rv);
     763             : 
     764             :       uint32_t requestStatus;
     765           1 :       rv = httpChannel->GetResponseStatus(&requestStatus);
     766           1 :       NS_ENSURE_SUCCESS(rv, rv);
     767           1 :       mozilla::Telemetry::Accumulate(mozilla::Telemetry::URLCLASSIFIER_UPDATE_REMOTE_STATUS2,
     768           1 :                                      mTelemetryProvider, HTTPStatusToBucket(requestStatus));
     769           1 :       LOG(("nsUrlClassifierStreamUpdater::OnStartRequest %s (%d)", succeeded ?
     770           2 :            "succeeded" : "failed", requestStatus));
     771           1 :       if (!succeeded) {
     772             :         // 404 or other error, pass error status back
     773           1 :         strStatus.AppendInt(requestStatus);
     774           1 :         downloadError = true;
     775             :       }
     776             :     }
     777             :   }
     778             : 
     779           1 :   if (downloadError) {
     780           1 :     LOG(("nsUrlClassifierStreamUpdater::Download error [this=%p]", this));
     781           1 :     mDownloadError = true;
     782           1 :     mDownloadErrorStatusStr = strStatus;
     783           1 :     status = NS_ERROR_ABORT;
     784           0 :   } else if (NS_SUCCEEDED(status)) {
     785           0 :     MOZ_ASSERT(mDownloadErrorCallback);
     786           0 :     mBeganStream = true;
     787           0 :     LOG(("nsUrlClassifierStreamUpdater::Beginning stream [this=%p]", this));
     788           0 :     rv = mDBService->BeginStream(mStreamTable);
     789           0 :     NS_ENSURE_SUCCESS(rv, rv);
     790             :   }
     791             : 
     792           1 :   mStreamTable.Truncate();
     793             : 
     794           1 :   return status;
     795             : }
     796             : 
     797             : NS_IMETHODIMP
     798           0 : nsUrlClassifierStreamUpdater::OnDataAvailable(nsIRequest *request,
     799             :                                               nsISupports* context,
     800             :                                               nsIInputStream *aIStream,
     801             :                                               uint64_t aSourceOffset,
     802             :                                               uint32_t aLength)
     803             : {
     804           0 :   if (!mDBService)
     805           0 :     return NS_ERROR_NOT_INITIALIZED;
     806             : 
     807           0 :   LOG(("OnDataAvailable (%d bytes)", aLength));
     808             : 
     809           0 :   if (aSourceOffset > MAX_FILE_SIZE) {
     810           0 :     LOG(("OnDataAvailable::Abort because exceeded the maximum file size(%" PRIu64 ")", aSourceOffset));
     811           0 :     return NS_ERROR_FILE_TOO_BIG;
     812             :   }
     813             : 
     814             :   nsresult rv;
     815             : 
     816             :   // Copy the data into a nsCString
     817           0 :   nsCString chunk;
     818           0 :   rv = NS_ConsumeStream(aIStream, aLength, chunk);
     819           0 :   NS_ENSURE_SUCCESS(rv, rv);
     820             : 
     821             :   //LOG(("Chunk (%d): %s\n\n", chunk.Length(), chunk.get()));
     822           0 :   rv = mDBService->UpdateStream(chunk);
     823           0 :   NS_ENSURE_SUCCESS(rv, rv);
     824             : 
     825           0 :   return NS_OK;
     826             : }
     827             : 
     828             : NS_IMETHODIMP
     829           1 : nsUrlClassifierStreamUpdater::OnStopRequest(nsIRequest *request, nsISupports* context,
     830             :                                             nsresult aStatus)
     831             : {
     832           1 :   if (!mDBService)
     833           0 :     return NS_ERROR_NOT_INITIALIZED;
     834             : 
     835           1 :   LOG(("OnStopRequest (status %" PRIx32 ", beganStream %s, this=%p)",
     836           1 :        static_cast<uint32_t>(aStatus), mBeganStream ? "true" : "false", this));
     837             : 
     838             :   nsresult rv;
     839             : 
     840           1 :   if (NS_SUCCEEDED(aStatus)) {
     841             :     // Success, finish this stream and move on to the next.
     842           0 :     rv = mDBService->FinishStream();
     843           1 :   } else if (mBeganStream) {
     844           0 :     LOG(("OnStopRequest::Canceling update [this=%p]", this));
     845             :     // We began this stream and couldn't finish it.  We have to cancel the
     846             :     // update, it's not in a consistent state.
     847           0 :     rv = mDBService->CancelUpdate();
     848             :   } else {
     849           1 :     LOG(("OnStopRequest::Finishing update [this=%p]", this));
     850             :     // The fetch failed, but we didn't start the stream (probably a
     851             :     // server or connection error).  We can commit what we've applied
     852             :     // so far, and request again later.
     853           1 :     rv = mDBService->FinishUpdate();
     854             :   }
     855             : 
     856           1 :   if (mResponseTimeoutTimer) {
     857           0 :     mResponseTimeoutTimer->Cancel();
     858           0 :     mResponseTimeoutTimer = nullptr;
     859             :   }
     860             : 
     861             :   // mResponseTimeoutTimer may be cleared in OnStartRequest, so we check mTimeoutTimer
     862             :   // to see whether the update was has timed out
     863           1 :   if (mTimeoutTimer) {
     864           1 :     mozilla::Telemetry::Accumulate(mozilla::Telemetry::URLCLASSIFIER_UPDATE_TIMEOUT,
     865             :                                    mTelemetryProvider,
     866           1 :                                    static_cast<uint8_t>(eNoTimeout));
     867           1 :     mTimeoutTimer->Cancel();
     868           1 :     mTimeoutTimer = nullptr;
     869             :   }
     870             : 
     871           1 :   mTelemetryProvider.Truncate();
     872           1 :   mTelemetryClockStart = 0;
     873           1 :   mChannel = nullptr;
     874             : 
     875             :   // If the fetch failed, return the network status rather than NS_OK, the
     876             :   // result of finishing a possibly-empty update
     877           1 :   if (NS_SUCCEEDED(aStatus)) {
     878           0 :     return rv;
     879             :   }
     880           1 :   return aStatus;
     881             : }
     882             : 
     883             : ///////////////////////////////////////////////////////////////////////////////
     884             : // nsIObserver implementation
     885             : 
     886             : NS_IMETHODIMP
     887           0 : nsUrlClassifierStreamUpdater::Observe(nsISupports *aSubject, const char *aTopic,
     888             :                                       const char16_t *aData)
     889             : {
     890           0 :   if (nsCRT::strcmp(aTopic, gQuitApplicationMessage) == 0) {
     891           0 :     if (mIsUpdating && mChannel) {
     892           0 :       LOG(("Cancel download"));
     893             :       nsresult rv;
     894           0 :       rv = mChannel->Cancel(NS_ERROR_ABORT);
     895           0 :       NS_ENSURE_SUCCESS(rv, rv);
     896           0 :       mIsUpdating = false;
     897           0 :       mChannel = nullptr;
     898           0 :       mTelemetryClockStart = 0;
     899             :     }
     900           0 :     if (mFetchIndirectUpdatesTimer) {
     901           0 :       mFetchIndirectUpdatesTimer->Cancel();
     902           0 :       mFetchIndirectUpdatesTimer = nullptr;
     903             :     }
     904           0 :     if (mFetchNextRequestTimer) {
     905           0 :       mFetchNextRequestTimer->Cancel();
     906           0 :       mFetchNextRequestTimer = nullptr;
     907             :     }
     908           0 :     if (mResponseTimeoutTimer) {
     909           0 :       mResponseTimeoutTimer->Cancel();
     910           0 :       mResponseTimeoutTimer = nullptr;
     911             :     }
     912           0 :     if (mTimeoutTimer) {
     913           0 :       mTimeoutTimer->Cancel();
     914           0 :       mTimeoutTimer = nullptr;
     915             :     }
     916             : 
     917             :   }
     918           0 :   return NS_OK;
     919             : }
     920             : 
     921             : ///////////////////////////////////////////////////////////////////////////////
     922             : // nsIInterfaceRequestor implementation
     923             : 
     924             : NS_IMETHODIMP
     925          18 : nsUrlClassifierStreamUpdater::GetInterface(const nsIID & eventSinkIID, void* *_retval)
     926             : {
     927          18 :   return QueryInterface(eventSinkIID, _retval);
     928             : }
     929             : 
     930             : 
     931             : ///////////////////////////////////////////////////////////////////////////////
     932             : // nsITimerCallback implementation
     933             : NS_IMETHODIMP
     934           0 : nsUrlClassifierStreamUpdater::Notify(nsITimer *timer)
     935             : {
     936           0 :   LOG(("nsUrlClassifierStreamUpdater::Notify [%p]", this));
     937             : 
     938           0 :   if (timer == mFetchNextRequestTimer) {
     939           0 :     mFetchNextRequestTimer = nullptr;
     940           0 :     FetchNextRequest();
     941           0 :     return NS_OK;
     942             :   }
     943             : 
     944           0 :   if (timer == mFetchIndirectUpdatesTimer) {
     945           0 :     mFetchIndirectUpdatesTimer = nullptr;
     946             :     // Start the update process up again.
     947           0 :     FetchNext();
     948           0 :     return NS_OK;
     949             :   }
     950             : 
     951           0 :   bool updateFailed = false;
     952           0 :   if (timer == mResponseTimeoutTimer) {
     953           0 :     mResponseTimeoutTimer = nullptr;
     954           0 :     if (mTimeoutTimer) {
     955           0 :       mTimeoutTimer->Cancel();
     956           0 :       mTimeoutTimer = nullptr;
     957             :     }
     958           0 :     mDownloadError = true; // Trigger backoff
     959           0 :     updateFailed = true;
     960           0 :     mozilla::Telemetry::Accumulate(mozilla::Telemetry::URLCLASSIFIER_UPDATE_TIMEOUT,
     961             :                                    mTelemetryProvider,
     962           0 :                                    static_cast<uint8_t>(eResponseTimeout));
     963             :   }
     964             : 
     965           0 :   if (timer == mTimeoutTimer) {
     966           0 :     mTimeoutTimer = nullptr;
     967             :     // No backoff since the connection may just be temporarily slow.
     968           0 :     updateFailed = true;
     969           0 :     mozilla::Telemetry::Accumulate(mozilla::Telemetry::URLCLASSIFIER_UPDATE_TIMEOUT,
     970             :                                    mTelemetryProvider,
     971           0 :                                    static_cast<uint8_t>(eDownloadTimeout));
     972             :   }
     973             : 
     974           0 :   if (updateFailed) {
     975             :     // Cancelling the channel will trigger OnStopRequest.
     976           0 :     mozilla::Unused << mChannel->Cancel(NS_ERROR_ABORT);
     977           0 :     mChannel = nullptr;
     978           0 :     mTelemetryClockStart = 0;
     979             : 
     980           0 :     if (mFetchIndirectUpdatesTimer) {
     981           0 :       mFetchIndirectUpdatesTimer->Cancel();
     982           0 :       mFetchIndirectUpdatesTimer = nullptr;
     983             :     }
     984           0 :     if (mFetchNextRequestTimer) {
     985           0 :       mFetchNextRequestTimer->Cancel();
     986           0 :       mFetchNextRequestTimer = nullptr;
     987             :     }
     988             : 
     989           0 :     return NS_OK;
     990             :   }
     991             : 
     992           0 :   MOZ_ASSERT_UNREACHABLE("A timer is fired from nowhere.");
     993             :   return NS_OK;
     994             : }
     995             : 

Generated by: LCOV version 1.13