LCOV - code coverage report
Current view: top level - netwerk/base - Predictor.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 557 1275 43.7 %
Date: 2017-07-14 16:53:18 Functions: 46 112 41.1 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* vim: set ts=2 sts=2 et sw=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 <algorithm>
       7             : 
       8             : #include "Predictor.h"
       9             : 
      10             : #include "nsAppDirectoryServiceDefs.h"
      11             : #include "nsICacheStorage.h"
      12             : #include "nsICacheStorageService.h"
      13             : #include "nsICachingChannel.h"
      14             : #include "nsICancelable.h"
      15             : #include "nsIChannel.h"
      16             : #include "nsContentUtils.h"
      17             : #include "nsIDNSService.h"
      18             : #include "nsIDocument.h"
      19             : #include "nsIFile.h"
      20             : #include "nsIHttpChannel.h"
      21             : #include "nsIInputStream.h"
      22             : #include "nsIIOService.h"
      23             : #include "nsILoadContext.h"
      24             : #include "nsILoadContextInfo.h"
      25             : #include "nsILoadGroup.h"
      26             : #include "nsINetworkPredictorVerifier.h"
      27             : #include "nsIObserverService.h"
      28             : #include "nsIPrefBranch.h"
      29             : #include "nsIPrefService.h"
      30             : #include "nsISpeculativeConnect.h"
      31             : #include "nsITimer.h"
      32             : #include "nsIURI.h"
      33             : #include "nsNetUtil.h"
      34             : #include "nsServiceManagerUtils.h"
      35             : #include "nsStreamUtils.h"
      36             : #include "nsString.h"
      37             : #include "nsThreadUtils.h"
      38             : #include "mozilla/Logging.h"
      39             : 
      40             : #include "mozilla/Preferences.h"
      41             : #include "mozilla/Telemetry.h"
      42             : 
      43             : #include "mozilla/net/NeckoCommon.h"
      44             : #include "mozilla/net/NeckoParent.h"
      45             : 
      46             : #include "LoadContextInfo.h"
      47             : #include "mozilla/ipc/URIUtils.h"
      48             : #include "SerializedLoadContext.h"
      49             : #include "mozilla/net/NeckoChild.h"
      50             : 
      51             : #include "mozilla/dom/ContentParent.h"
      52             : #include "mozilla/ClearOnShutdown.h"
      53             : 
      54             : using namespace mozilla;
      55             : 
      56             : namespace mozilla {
      57             : namespace net {
      58             : 
      59             : Predictor *Predictor::sSelf = nullptr;
      60             : 
      61             : static LazyLogModule gPredictorLog("NetworkPredictor");
      62             : 
      63             : #define PREDICTOR_LOG(args) MOZ_LOG(gPredictorLog, mozilla::LogLevel::Debug, args)
      64             : 
      65             : #define RETURN_IF_FAILED(_rv) \
      66             :   do { \
      67             :     if (NS_FAILED(_rv)) { \
      68             :       return; \
      69             :     } \
      70             :   } while (0)
      71             : 
      72             : #define NOW_IN_SECONDS() static_cast<uint32_t>(PR_Now() / PR_USEC_PER_SEC)
      73             : 
      74             : 
      75             : static const char PREDICTOR_ENABLED_PREF[] = "network.predictor.enabled";
      76             : static const char PREDICTOR_SSL_HOVER_PREF[] = "network.predictor.enable-hover-on-ssl";
      77             : static const char PREDICTOR_PREFETCH_PREF[] = "network.predictor.enable-prefetch";
      78             : 
      79             : static const char PREDICTOR_PAGE_DELTA_DAY_PREF[] =
      80             :   "network.predictor.page-degradation.day";
      81             : static const int32_t PREDICTOR_PAGE_DELTA_DAY_DEFAULT = 0;
      82             : static const char PREDICTOR_PAGE_DELTA_WEEK_PREF[] =
      83             :   "network.predictor.page-degradation.week";
      84             : static const int32_t PREDICTOR_PAGE_DELTA_WEEK_DEFAULT = 5;
      85             : static const char PREDICTOR_PAGE_DELTA_MONTH_PREF[] =
      86             :   "network.predictor.page-degradation.month";
      87             : static const int32_t PREDICTOR_PAGE_DELTA_MONTH_DEFAULT = 10;
      88             : static const char PREDICTOR_PAGE_DELTA_YEAR_PREF[] =
      89             :   "network.predictor.page-degradation.year";
      90             : static const int32_t PREDICTOR_PAGE_DELTA_YEAR_DEFAULT = 25;
      91             : static const char PREDICTOR_PAGE_DELTA_MAX_PREF[] =
      92             :   "network.predictor.page-degradation.max";
      93             : static const int32_t PREDICTOR_PAGE_DELTA_MAX_DEFAULT = 50;
      94             : static const char PREDICTOR_SUB_DELTA_DAY_PREF[] =
      95             :   "network.predictor.subresource-degradation.day";
      96             : static const int32_t PREDICTOR_SUB_DELTA_DAY_DEFAULT = 1;
      97             : static const char PREDICTOR_SUB_DELTA_WEEK_PREF[] =
      98             :   "network.predictor.subresource-degradation.week";
      99             : static const int32_t PREDICTOR_SUB_DELTA_WEEK_DEFAULT = 10;
     100             : static const char PREDICTOR_SUB_DELTA_MONTH_PREF[] =
     101             :   "network.predictor.subresource-degradation.month";
     102             : static const int32_t PREDICTOR_SUB_DELTA_MONTH_DEFAULT = 25;
     103             : static const char PREDICTOR_SUB_DELTA_YEAR_PREF[] =
     104             :   "network.predictor.subresource-degradation.year";
     105             : static const int32_t PREDICTOR_SUB_DELTA_YEAR_DEFAULT = 50;
     106             : static const char PREDICTOR_SUB_DELTA_MAX_PREF[] =
     107             :   "network.predictor.subresource-degradation.max";
     108             : static const int32_t PREDICTOR_SUB_DELTA_MAX_DEFAULT = 100;
     109             : 
     110             : static const char PREDICTOR_PREFETCH_ROLLING_LOAD_PREF[] =
     111             :   "network.predictor.prefetch-rolling-load-count";
     112             : static const int32_t PREFETCH_ROLLING_LOAD_DEFAULT = 10;
     113             : static const char PREDICTOR_PREFETCH_MIN_PREF[] =
     114             :   "network.predictor.prefetch-min-confidence";
     115             : static const int32_t PREFETCH_MIN_DEFAULT = 100;
     116             : static const char PREDICTOR_PRECONNECT_MIN_PREF[] =
     117             :   "network.predictor.preconnect-min-confidence";
     118             : static const int32_t PRECONNECT_MIN_DEFAULT = 90;
     119             : static const char PREDICTOR_PRERESOLVE_MIN_PREF[] =
     120             :   "network.predictor.preresolve-min-confidence";
     121             : static const int32_t PRERESOLVE_MIN_DEFAULT = 60;
     122             : static const char PREDICTOR_REDIRECT_LIKELY_PREF[] =
     123             :   "network.predictor.redirect-likely-confidence";
     124             : static const int32_t REDIRECT_LIKELY_DEFAULT = 75;
     125             : 
     126             : static const char PREDICTOR_PREFETCH_FORCE_VALID_PREF[] =
     127             :   "network.predictor.prefetch-force-valid-for";
     128             : static const int32_t PREFETCH_FORCE_VALID_DEFAULT = 10;
     129             : 
     130             : static const char PREDICTOR_MAX_RESOURCES_PREF[] =
     131             :   "network.predictor.max-resources-per-entry";
     132             : static const uint32_t PREDICTOR_MAX_RESOURCES_DEFAULT = 100;
     133             : 
     134             : // This is selected in concert with max-resources-per-entry to keep memory usage
     135             : // low-ish. The default of the combo of the two is ~50k
     136             : static const char PREDICTOR_MAX_URI_LENGTH_PREF[] =
     137             :   "network.predictor.max-uri-length";
     138             : static const uint32_t PREDICTOR_MAX_URI_LENGTH_DEFAULT = 500;
     139             : 
     140             : static const char PREDICTOR_DOING_TESTS_PREF[] = "network.predictor.doing-tests";
     141             : 
     142             : static const char PREDICTOR_CLEANED_UP_PREF[] = "network.predictor.cleaned-up";
     143             : 
     144             : // All these time values are in sec
     145             : static const uint32_t ONE_DAY = 86400U;
     146             : static const uint32_t ONE_WEEK = 7U * ONE_DAY;
     147             : static const uint32_t ONE_MONTH = 30U * ONE_DAY;
     148             : static const uint32_t ONE_YEAR = 365U * ONE_DAY;
     149             : 
     150             : static const uint32_t STARTUP_WINDOW = 5U * 60U; // 5min
     151             : 
     152             : // Version of metadata entries we expect
     153             : static const uint32_t METADATA_VERSION = 1;
     154             : 
     155             : // Flags available in entries
     156             : // FLAG_PREFETCHABLE - we have determined that this item is eligible for prefetch
     157             : static const uint32_t FLAG_PREFETCHABLE = 1 << 0;
     158             : 
     159             : // We save 12 bits in the "flags" section of our metadata for actual flags, the
     160             : // rest are to keep track of a rolling count of which loads a resource has been
     161             : // used on to determine if we can prefetch that resource or not;
     162             : static const uint8_t kRollingLoadOffset = 12;
     163             : static const int32_t kMaxPrefetchRollingLoadCount = 20;
     164             : static const uint32_t kFlagsMask = ((1 << kRollingLoadOffset) - 1);
     165             : 
     166             : // ID Extensions for cache entries
     167             : #define PREDICTOR_ORIGIN_EXTENSION "predictor-origin"
     168             : 
     169             : // Get the full origin (scheme, host, port) out of a URI (maybe should be part
     170             : // of nsIURI instead?)
     171             : static nsresult
     172           6 : ExtractOrigin(nsIURI *uri, nsIURI **originUri, nsIIOService *ioService)
     173             : {
     174          12 :   nsAutoCString s;
     175           6 :   s.Truncate();
     176           6 :   nsresult rv = nsContentUtils::GetASCIIOrigin(uri, s);
     177           6 :   NS_ENSURE_SUCCESS(rv, rv);
     178             : 
     179           6 :   return NS_NewURI(originUri, s, nullptr, nullptr, ioService);
     180             : }
     181             : 
     182             : // All URIs we get passed *must* be http or https if they're not null. This
     183             : // helps ensure that.
     184             : static bool
     185          98 : IsNullOrHttp(nsIURI *uri)
     186             : {
     187          98 :   if (!uri) {
     188           4 :     return true;
     189             :   }
     190             : 
     191          94 :   bool isHTTP = false;
     192          94 :   uri->SchemeIs("http", &isHTTP);
     193          94 :   if (!isHTTP) {
     194          81 :     uri->SchemeIs("https", &isHTTP);
     195             :   }
     196             : 
     197          94 :   return isHTTP;
     198             : }
     199             : 
     200             : // Listener for the speculative DNS requests we'll fire off, which just ignores
     201             : // the result (since we're just trying to warm the cache). This also exists to
     202             : // reduce round-trips to the main thread, by being something threadsafe the
     203             : // Predictor can use.
     204             : 
     205           1 : NS_IMPL_ISUPPORTS(Predictor::DNSListener, nsIDNSListener);
     206             : 
     207             : NS_IMETHODIMP
     208           0 : Predictor::DNSListener::OnLookupComplete(nsICancelable *request,
     209             :                                          nsIDNSRecord *rec,
     210             :                                          nsresult status)
     211             : {
     212           0 :   return NS_OK;
     213             : }
     214             : 
     215             : // Class to proxy important information from the initial predictor call through
     216             : // the cache API and back into the internals of the predictor. We can't use the
     217             : // predictor itself, as it may have multiple actions in-flight, and each action
     218             : // has different parameters.
     219          92 : NS_IMPL_ISUPPORTS(Predictor::Action, nsICacheEntryOpenCallback);
     220             : 
     221           8 : Predictor::Action::Action(bool fullUri, bool predict,
     222             :                           Predictor::Reason reason,
     223             :                           nsIURI *targetURI, nsIURI *sourceURI,
     224             :                           nsINetworkPredictorVerifier *verifier,
     225           8 :                           Predictor *predictor)
     226             :   :mFullUri(fullUri)
     227             :   ,mPredict(predict)
     228             :   ,mTargetURI(targetURI)
     229             :   ,mSourceURI(sourceURI)
     230             :   ,mVerifier(verifier)
     231             :   ,mStackCount(0)
     232           8 :   ,mPredictor(predictor)
     233             : {
     234           8 :   mStartTime = TimeStamp::Now();
     235           8 :   if (mPredict) {
     236           2 :     mPredictReason = reason.mPredict;
     237             :   } else {
     238           6 :     mLearnReason = reason.mLearn;
     239             :   }
     240           8 : }
     241             : 
     242           0 : Predictor::Action::Action(bool fullUri, bool predict,
     243             :                           Predictor::Reason reason,
     244             :                           nsIURI *targetURI, nsIURI *sourceURI,
     245             :                           nsINetworkPredictorVerifier *verifier,
     246           0 :                           Predictor *predictor, uint8_t stackCount)
     247             :   :mFullUri(fullUri)
     248             :   ,mPredict(predict)
     249             :   ,mTargetURI(targetURI)
     250             :   ,mSourceURI(sourceURI)
     251             :   ,mVerifier(verifier)
     252             :   ,mStackCount(stackCount)
     253           0 :   ,mPredictor(predictor)
     254             : {
     255           0 :   mStartTime = TimeStamp::Now();
     256           0 :   if (mPredict) {
     257           0 :     mPredictReason = reason.mPredict;
     258             :   } else {
     259           0 :     mLearnReason = reason.mLearn;
     260             :   }
     261           0 : }
     262             : 
     263          16 : Predictor::Action::~Action()
     264          24 : { }
     265             : 
     266             : NS_IMETHODIMP
     267           6 : Predictor::Action::OnCacheEntryCheck(nsICacheEntry *entry,
     268             :                                      nsIApplicationCache *appCache,
     269             :                                      uint32_t *result)
     270             : {
     271           6 :   *result = nsICacheEntryOpenCallback::ENTRY_WANTED;
     272           6 :   return NS_OK;
     273             : }
     274             : 
     275             : NS_IMETHODIMP
     276           8 : Predictor::Action::OnCacheEntryAvailable(nsICacheEntry *entry, bool isNew,
     277             :                                          nsIApplicationCache *appCache,
     278             :                                          nsresult result)
     279             : {
     280           8 :   MOZ_ASSERT(NS_IsMainThread(), "Got cache entry off main thread!");
     281             : 
     282          16 :   nsAutoCString targetURI, sourceURI;
     283           8 :   mTargetURI->GetAsciiSpec(targetURI);
     284           8 :   if (mSourceURI) {
     285           4 :     mSourceURI->GetAsciiSpec(sourceURI);
     286             :   }
     287           8 :   PREDICTOR_LOG(("OnCacheEntryAvailable %p called. entry=%p mFullUri=%d mPredict=%d "
     288             :                  "mPredictReason=%d mLearnReason=%d mTargetURI=%s "
     289             :                  "mSourceURI=%s mStackCount=%d isNew=%d result=0x%08" PRIx32,
     290             :                  this, entry, mFullUri, mPredict, mPredictReason, mLearnReason,
     291             :                  targetURI.get(), sourceURI.get(), mStackCount,
     292             :                  isNew, static_cast<uint32_t>(result)));
     293           8 :   if (NS_FAILED(result)) {
     294           2 :     PREDICTOR_LOG(("OnCacheEntryAvailable %p FAILED to get cache entry (0x%08" PRIX32
     295             :                    "). Aborting.", this, static_cast<uint32_t>(result)));
     296           2 :     return NS_OK;
     297             :   }
     298           6 :   Telemetry::AccumulateTimeDelta(Telemetry::PREDICTOR_WAIT_TIME,
     299           6 :                                  mStartTime);
     300           6 :   if (mPredict) {
     301           2 :     bool predicted = mPredictor->PredictInternal(mPredictReason, entry, isNew,
     302           1 :                                                  mFullUri, mTargetURI,
     303           2 :                                                  mVerifier, mStackCount);
     304           1 :     Telemetry::AccumulateTimeDelta(
     305           1 :       Telemetry::PREDICTOR_PREDICT_WORK_TIME, mStartTime);
     306           1 :     if (predicted) {
     307           0 :       Telemetry::AccumulateTimeDelta(
     308           0 :         Telemetry::PREDICTOR_PREDICT_TIME_TO_ACTION, mStartTime);
     309             :     } else {
     310           1 :       Telemetry::AccumulateTimeDelta(
     311           1 :         Telemetry::PREDICTOR_PREDICT_TIME_TO_INACTION, mStartTime);
     312             :     }
     313             :   } else {
     314           5 :     mPredictor->LearnInternal(mLearnReason, entry, isNew, mFullUri, mTargetURI,
     315           5 :                               mSourceURI);
     316           5 :     Telemetry::AccumulateTimeDelta(
     317           5 :       Telemetry::PREDICTOR_LEARN_WORK_TIME, mStartTime);
     318             :   }
     319             : 
     320           6 :   return NS_OK;
     321             : }
     322             : 
     323          72 : NS_IMPL_ISUPPORTS(Predictor,
     324             :                   nsINetworkPredictor,
     325             :                   nsIObserver,
     326             :                   nsISpeculativeConnectionOverrider,
     327             :                   nsIInterfaceRequestor,
     328             :                   nsICacheEntryMetaDataVisitor,
     329             :                   nsINetworkPredictorVerifier)
     330             : 
     331           2 : Predictor::Predictor()
     332             :   :mInitialized(false)
     333             :   ,mEnabled(true)
     334             :   ,mEnableHoverOnSSL(false)
     335             :   ,mEnablePrefetch(true)
     336             :   ,mPageDegradationDay(PREDICTOR_PAGE_DELTA_DAY_DEFAULT)
     337             :   ,mPageDegradationWeek(PREDICTOR_PAGE_DELTA_WEEK_DEFAULT)
     338             :   ,mPageDegradationMonth(PREDICTOR_PAGE_DELTA_MONTH_DEFAULT)
     339             :   ,mPageDegradationYear(PREDICTOR_PAGE_DELTA_YEAR_DEFAULT)
     340             :   ,mPageDegradationMax(PREDICTOR_PAGE_DELTA_MAX_DEFAULT)
     341             :   ,mSubresourceDegradationDay(PREDICTOR_SUB_DELTA_DAY_DEFAULT)
     342             :   ,mSubresourceDegradationWeek(PREDICTOR_SUB_DELTA_WEEK_DEFAULT)
     343             :   ,mSubresourceDegradationMonth(PREDICTOR_SUB_DELTA_MONTH_DEFAULT)
     344             :   ,mSubresourceDegradationYear(PREDICTOR_SUB_DELTA_YEAR_DEFAULT)
     345             :   ,mSubresourceDegradationMax(PREDICTOR_SUB_DELTA_MAX_DEFAULT)
     346             :   ,mPrefetchRollingLoadCount(PREFETCH_ROLLING_LOAD_DEFAULT)
     347             :   ,mPrefetchMinConfidence(PREFETCH_MIN_DEFAULT)
     348             :   ,mPreconnectMinConfidence(PRECONNECT_MIN_DEFAULT)
     349             :   ,mPreresolveMinConfidence(PRERESOLVE_MIN_DEFAULT)
     350             :   ,mRedirectLikelyConfidence(REDIRECT_LIKELY_DEFAULT)
     351             :   ,mPrefetchForceValidFor(PREFETCH_FORCE_VALID_DEFAULT)
     352             :   ,mMaxResourcesPerEntry(PREDICTOR_MAX_RESOURCES_DEFAULT)
     353             :   ,mStartupCount(1)
     354             :   ,mMaxURILength(PREDICTOR_MAX_URI_LENGTH_DEFAULT)
     355           2 :   ,mDoingTests(false)
     356             : {
     357           2 :   MOZ_ASSERT(!sSelf, "multiple Predictor instances!");
     358           2 :   sSelf = this;
     359           2 : }
     360             : 
     361           0 : Predictor::~Predictor()
     362             : {
     363           0 :   if (mInitialized)
     364           0 :     Shutdown();
     365             : 
     366           0 :   sSelf = nullptr;
     367           0 : }
     368             : 
     369             : // Predictor::nsIObserver
     370             : 
     371             : nsresult
     372           1 : Predictor::InstallObserver()
     373             : {
     374           1 :   MOZ_ASSERT(NS_IsMainThread(), "Installing observer off main thread");
     375             : 
     376           1 :   nsresult rv = NS_OK;
     377             :   nsCOMPtr<nsIObserverService> obs =
     378           2 :     mozilla::services::GetObserverService();
     379           1 :   if (!obs) {
     380           0 :     return NS_ERROR_NOT_AVAILABLE;
     381             :   }
     382             : 
     383           1 :   rv = obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
     384           1 :   NS_ENSURE_SUCCESS(rv, rv);
     385             : 
     386           1 :   Preferences::AddBoolVarCache(&mEnabled, PREDICTOR_ENABLED_PREF, true);
     387           1 :   Preferences::AddBoolVarCache(&mEnableHoverOnSSL,
     388           1 :                                PREDICTOR_SSL_HOVER_PREF, false);
     389           1 :   Preferences::AddBoolVarCache(&mEnablePrefetch, PREDICTOR_PREFETCH_PREF, true);
     390           1 :   Preferences::AddIntVarCache(&mPageDegradationDay,
     391             :                               PREDICTOR_PAGE_DELTA_DAY_PREF,
     392           1 :                               PREDICTOR_PAGE_DELTA_DAY_DEFAULT);
     393           1 :   Preferences::AddIntVarCache(&mPageDegradationWeek,
     394             :                               PREDICTOR_PAGE_DELTA_WEEK_PREF,
     395           1 :                               PREDICTOR_PAGE_DELTA_WEEK_DEFAULT);
     396           1 :   Preferences::AddIntVarCache(&mPageDegradationMonth,
     397             :                               PREDICTOR_PAGE_DELTA_MONTH_PREF,
     398           1 :                               PREDICTOR_PAGE_DELTA_MONTH_DEFAULT);
     399           1 :   Preferences::AddIntVarCache(&mPageDegradationYear,
     400             :                               PREDICTOR_PAGE_DELTA_YEAR_PREF,
     401           1 :                               PREDICTOR_PAGE_DELTA_YEAR_DEFAULT);
     402           1 :   Preferences::AddIntVarCache(&mPageDegradationMax,
     403             :                               PREDICTOR_PAGE_DELTA_MAX_PREF,
     404           1 :                               PREDICTOR_PAGE_DELTA_MAX_DEFAULT);
     405             : 
     406           1 :   Preferences::AddIntVarCache(&mSubresourceDegradationDay,
     407             :                               PREDICTOR_SUB_DELTA_DAY_PREF,
     408           1 :                               PREDICTOR_SUB_DELTA_DAY_DEFAULT);
     409           1 :   Preferences::AddIntVarCache(&mSubresourceDegradationWeek,
     410             :                               PREDICTOR_SUB_DELTA_WEEK_PREF,
     411           1 :                               PREDICTOR_SUB_DELTA_WEEK_DEFAULT);
     412           1 :   Preferences::AddIntVarCache(&mSubresourceDegradationMonth,
     413             :                               PREDICTOR_SUB_DELTA_MONTH_PREF,
     414           1 :                               PREDICTOR_SUB_DELTA_MONTH_DEFAULT);
     415           1 :   Preferences::AddIntVarCache(&mSubresourceDegradationYear,
     416             :                               PREDICTOR_SUB_DELTA_YEAR_PREF,
     417           1 :                               PREDICTOR_SUB_DELTA_YEAR_DEFAULT);
     418           1 :   Preferences::AddIntVarCache(&mSubresourceDegradationMax,
     419             :                               PREDICTOR_SUB_DELTA_MAX_PREF,
     420           1 :                               PREDICTOR_SUB_DELTA_MAX_DEFAULT);
     421             : 
     422           1 :   Preferences::AddIntVarCache(&mPrefetchRollingLoadCount,
     423             :                               PREDICTOR_PREFETCH_ROLLING_LOAD_PREF,
     424           1 :                               PREFETCH_ROLLING_LOAD_DEFAULT);
     425           1 :   Preferences::AddIntVarCache(&mPrefetchMinConfidence,
     426             :                               PREDICTOR_PREFETCH_MIN_PREF,
     427           1 :                               PREFETCH_MIN_DEFAULT);
     428           1 :   Preferences::AddIntVarCache(&mPreconnectMinConfidence,
     429             :                               PREDICTOR_PRECONNECT_MIN_PREF,
     430           1 :                               PRECONNECT_MIN_DEFAULT);
     431           1 :   Preferences::AddIntVarCache(&mPreresolveMinConfidence,
     432             :                               PREDICTOR_PRERESOLVE_MIN_PREF,
     433           1 :                               PRERESOLVE_MIN_DEFAULT);
     434           1 :   Preferences::AddIntVarCache(&mRedirectLikelyConfidence,
     435             :                               PREDICTOR_REDIRECT_LIKELY_PREF,
     436           1 :                               REDIRECT_LIKELY_DEFAULT);
     437             : 
     438           1 :   Preferences::AddIntVarCache(&mPrefetchForceValidFor,
     439             :                               PREDICTOR_PREFETCH_FORCE_VALID_PREF,
     440           1 :                               PREFETCH_FORCE_VALID_DEFAULT);
     441             : 
     442           1 :   Preferences::AddIntVarCache(&mMaxResourcesPerEntry,
     443             :                               PREDICTOR_MAX_RESOURCES_PREF,
     444           1 :                               PREDICTOR_MAX_RESOURCES_DEFAULT);
     445             : 
     446           1 :   Preferences::AddBoolVarCache(&mCleanedUp, PREDICTOR_CLEANED_UP_PREF, false);
     447             : 
     448           1 :   Preferences::AddUintVarCache(&mMaxURILength, PREDICTOR_MAX_URI_LENGTH_PREF,
     449           1 :                                PREDICTOR_MAX_URI_LENGTH_DEFAULT);
     450             : 
     451           1 :   Preferences::AddBoolVarCache(&mDoingTests, PREDICTOR_DOING_TESTS_PREF, false);
     452             : 
     453           1 :   if (!mCleanedUp) {
     454           1 :     mCleanupTimer = do_CreateInstance("@mozilla.org/timer;1");
     455           1 :     mCleanupTimer->Init(this, 60 * 1000, nsITimer::TYPE_ONE_SHOT);
     456             :   }
     457             : 
     458           1 :   return rv;
     459             : }
     460             : 
     461             : void
     462           0 : Predictor::RemoveObserver()
     463             : {
     464           0 :   MOZ_ASSERT(NS_IsMainThread(), "Removing observer off main thread");
     465             : 
     466             :   nsCOMPtr<nsIObserverService> obs =
     467           0 :     mozilla::services::GetObserverService();
     468           0 :   if (obs) {
     469           0 :     obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
     470             :   }
     471             : 
     472           0 :   if (mCleanupTimer) {
     473           0 :     mCleanupTimer->Cancel();
     474           0 :     mCleanupTimer = nullptr;
     475             :   }
     476           0 : }
     477             : 
     478             : NS_IMETHODIMP
     479           0 : Predictor::Observe(nsISupports *subject, const char *topic,
     480             :                    const char16_t *data_unicode)
     481             : {
     482           0 :   nsresult rv = NS_OK;
     483           0 :   MOZ_ASSERT(NS_IsMainThread(),
     484             :              "Predictor observing something off main thread!");
     485             : 
     486           0 :   if (!strcmp(NS_XPCOM_SHUTDOWN_OBSERVER_ID, topic)) {
     487           0 :     Shutdown();
     488           0 :   } else if (!strcmp("timer-callback", topic)) {
     489           0 :     MaybeCleanupOldDBFiles();
     490           0 :     mCleanupTimer = nullptr;
     491             :   }
     492             : 
     493           0 :   return rv;
     494             : }
     495             : 
     496             : // Predictor::nsISpeculativeConnectionOverrider
     497             : 
     498             : NS_IMETHODIMP
     499           0 : Predictor::GetIgnoreIdle(bool *ignoreIdle)
     500             : {
     501           0 :   *ignoreIdle = true;
     502           0 :   return NS_OK;
     503             : }
     504             : 
     505             : NS_IMETHODIMP
     506           0 : Predictor::GetParallelSpeculativeConnectLimit(
     507             :     uint32_t *parallelSpeculativeConnectLimit)
     508             : {
     509           0 :   *parallelSpeculativeConnectLimit = 6;
     510           0 :   return NS_OK;
     511             : }
     512             : 
     513             : NS_IMETHODIMP
     514           0 : Predictor::GetIsFromPredictor(bool *isFromPredictor)
     515             : {
     516           0 :   *isFromPredictor = true;
     517           0 :   return NS_OK;
     518             : }
     519             : 
     520             : NS_IMETHODIMP
     521           0 : Predictor::GetAllow1918(bool *allow1918)
     522             : {
     523           0 :   *allow1918 = false;
     524           0 :   return NS_OK;
     525             : }
     526             : 
     527             : // Predictor::nsIInterfaceRequestor
     528             : 
     529             : NS_IMETHODIMP
     530           0 : Predictor::GetInterface(const nsIID &iid, void **result)
     531             : {
     532           0 :   return QueryInterface(iid, result);
     533             : }
     534             : 
     535             : // Predictor::nsICacheEntryMetaDataVisitor
     536             : 
     537             : #define SEEN_META_DATA "predictor::seen"
     538             : #define RESOURCE_META_DATA "predictor::resource-count"
     539             : #define META_DATA_PREFIX "predictor::"
     540             : 
     541             : static bool
     542           3 : IsURIMetadataElement(const char *key)
     543             : {
     544           9 :   return StringBeginsWith(nsDependentCString(key),
     545          18 :                           NS_LITERAL_CSTRING(META_DATA_PREFIX)) &&
     546          22 :          !NS_LITERAL_CSTRING(SEEN_META_DATA).Equals(key) &&
     547          10 :          !NS_LITERAL_CSTRING(RESOURCE_META_DATA).Equals(key);
     548             : }
     549             : 
     550             : nsresult
     551           3 : Predictor::OnMetaDataElement(const char *asciiKey, const char *asciiValue)
     552             : {
     553           3 :   MOZ_ASSERT(NS_IsMainThread());
     554             : 
     555           3 :   if (!IsURIMetadataElement(asciiKey)) {
     556             :     // This isn't a bit of metadata we care about
     557           2 :     return NS_OK;
     558             :   }
     559             : 
     560           2 :   nsCString key, value;
     561           1 :   key.AssignASCII(asciiKey);
     562           1 :   value.AssignASCII(asciiValue);
     563           1 :   mKeysToOperateOn.AppendElement(key);
     564           1 :   mValuesToOperateOn.AppendElement(value);
     565             : 
     566           1 :   return NS_OK;
     567             : }
     568             : 
     569             : // Predictor::nsINetworkPredictor
     570             : 
     571             : nsresult
     572           1 : Predictor::Init()
     573             : {
     574           1 :   MOZ_DIAGNOSTIC_ASSERT(!IsNeckoChild());
     575             : 
     576           1 :   if (!NS_IsMainThread()) {
     577           0 :     MOZ_ASSERT(false, "Predictor::Init called off the main thread!");
     578             :     return NS_ERROR_UNEXPECTED;
     579             :   }
     580             : 
     581           1 :   nsresult rv = NS_OK;
     582             : 
     583           1 :   rv = InstallObserver();
     584           1 :   NS_ENSURE_SUCCESS(rv, rv);
     585             : 
     586           1 :   mLastStartupTime = mStartupTime = NOW_IN_SECONDS();
     587             : 
     588           1 :   if (!mDNSListener) {
     589           1 :     mDNSListener = new DNSListener();
     590             :   }
     591             : 
     592             :   mCacheStorageService =
     593           1 :     do_GetService("@mozilla.org/netwerk/cache-storage-service;1", &rv);
     594           1 :   NS_ENSURE_SUCCESS(rv, rv);
     595             : 
     596           1 :   mIOService = do_GetService("@mozilla.org/network/io-service;1", &rv);
     597           1 :   NS_ENSURE_SUCCESS(rv, rv);
     598             : 
     599           1 :   rv = NS_NewURI(getter_AddRefs(mStartupURI),
     600             :                  "predictor://startup", nullptr, mIOService);
     601           1 :   NS_ENSURE_SUCCESS(rv, rv);
     602             : 
     603           1 :   mSpeculativeService = do_QueryInterface(mIOService, &rv);
     604           1 :   NS_ENSURE_SUCCESS(rv, rv);
     605             : 
     606           1 :   mDnsService = do_GetService("@mozilla.org/network/dns-service;1", &rv);
     607           1 :   NS_ENSURE_SUCCESS(rv, rv);
     608             : 
     609           1 :   mInitialized = true;
     610             : 
     611           1 :   return rv;
     612             : }
     613             : 
     614             : namespace {
     615             : class PredictorThreadShutdownRunner : public Runnable
     616             : {
     617             : public:
     618           0 :   PredictorThreadShutdownRunner(nsIThread* ioThread, bool success)
     619           0 :     : Runnable("net::PredictorThreadShutdownRunner")
     620             :     , mIOThread(ioThread)
     621           0 :     , mSuccess(success)
     622           0 :   { }
     623           0 :   ~PredictorThreadShutdownRunner() { }
     624             : 
     625           0 :   NS_IMETHOD Run() override
     626             :   {
     627           0 :     MOZ_ASSERT(NS_IsMainThread(), "Shutting down io thread off main thread!");
     628           0 :     if (mSuccess) {
     629             :       // This means the cleanup happened. Mark so we don't try in the
     630             :       // future.
     631           0 :       Preferences::SetBool(PREDICTOR_CLEANED_UP_PREF, true);
     632             :     }
     633           0 :     return mIOThread->AsyncShutdown();
     634             :   }
     635             : 
     636             : private:
     637             :   nsCOMPtr<nsIThread> mIOThread;
     638             :   bool mSuccess;
     639             : };
     640             : 
     641             : class PredictorOldCleanupRunner : public Runnable
     642             : {
     643             : public:
     644           0 :   PredictorOldCleanupRunner(nsIThread* ioThread, nsIFile* dbFile)
     645           0 :     : Runnable("net::PredictorOldCleanupRunner")
     646             :     , mIOThread(ioThread)
     647           0 :     , mDBFile(dbFile)
     648           0 :   { }
     649             : 
     650           0 :   ~PredictorOldCleanupRunner() { }
     651             : 
     652           0 :   NS_IMETHOD Run() override
     653             :   {
     654           0 :     MOZ_ASSERT(!NS_IsMainThread(), "Cleaning up old files on main thread!");
     655           0 :     nsresult rv = CheckForAndDeleteOldDBFiles();
     656             :     RefPtr<PredictorThreadShutdownRunner> runner =
     657           0 :       new PredictorThreadShutdownRunner(mIOThread, NS_SUCCEEDED(rv));
     658           0 :     NS_DispatchToMainThread(runner);
     659           0 :     return NS_OK;
     660             :   }
     661             : 
     662             : private:
     663           0 :   nsresult CheckForAndDeleteOldDBFiles()
     664             :   {
     665           0 :     nsCOMPtr<nsIFile> oldDBFile;
     666           0 :     nsresult rv = mDBFile->GetParent(getter_AddRefs(oldDBFile));
     667           0 :     NS_ENSURE_SUCCESS(rv, rv);
     668             : 
     669           0 :     rv = oldDBFile->AppendNative(NS_LITERAL_CSTRING("seer.sqlite"));
     670           0 :     NS_ENSURE_SUCCESS(rv, rv);
     671             : 
     672           0 :     bool fileExists = false;
     673           0 :     rv = oldDBFile->Exists(&fileExists);
     674           0 :     NS_ENSURE_SUCCESS(rv, rv);
     675             : 
     676           0 :     if (fileExists) {
     677           0 :       rv = oldDBFile->Remove(false);
     678           0 :       NS_ENSURE_SUCCESS(rv, rv);
     679             :     }
     680             : 
     681           0 :     fileExists = false;
     682           0 :     rv = mDBFile->Exists(&fileExists);
     683           0 :     NS_ENSURE_SUCCESS(rv, rv);
     684             : 
     685           0 :     if (fileExists) {
     686           0 :       rv = mDBFile->Remove(false);
     687             :     }
     688             : 
     689           0 :     return rv;
     690             :   }
     691             : 
     692             :   nsCOMPtr<nsIThread> mIOThread;
     693             :   nsCOMPtr<nsIFile> mDBFile;
     694             : };
     695             : 
     696             : class PredictorLearnRunnable final : public Runnable {
     697             : public:
     698           3 :   PredictorLearnRunnable(nsIURI *targetURI, nsIURI *sourceURI,
     699             :                          PredictorLearnReason reason, const OriginAttributes &oa)
     700           3 :     : Runnable("PredictorLearnRunnable")
     701             :     , mTargetURI(targetURI)
     702             :     , mSourceURI(sourceURI)
     703             :     , mReason(reason)
     704           3 :     , mOA(oa)
     705           3 :   { }
     706             : 
     707           9 :   ~PredictorLearnRunnable() { }
     708             : 
     709           3 :   NS_IMETHOD Run() override
     710             :   {
     711           3 :     if (!gNeckoChild) {
     712             :       // This may have gone away between when this runnable was dispatched and
     713             :       // when it actually runs, so let's be safe here, even though we asserted
     714             :       // earlier.
     715           0 :       PREDICTOR_LOG(("predictor::learn (async) gNeckoChild went away"));
     716           0 :       return NS_OK;
     717             :     }
     718             : 
     719           6 :     ipc::URIParams serTargetURI;
     720           3 :     SerializeURI(mTargetURI, serTargetURI);
     721             : 
     722           6 :     ipc::OptionalURIParams serSourceURI;
     723           3 :     SerializeURI(mSourceURI, serSourceURI);
     724             : 
     725           3 :     PREDICTOR_LOG(("predictor::learn (async) forwarding to parent"));
     726           3 :     gNeckoChild->SendPredLearn(serTargetURI, serSourceURI, mReason, mOA);
     727             : 
     728           3 :     return NS_OK;
     729             :   }
     730             : 
     731             : private:
     732             :   nsCOMPtr<nsIURI> mTargetURI;
     733             :   nsCOMPtr<nsIURI> mSourceURI;
     734             :   PredictorLearnReason mReason;
     735             :   const OriginAttributes mOA;
     736             : };
     737             : 
     738             : } // namespace
     739             : 
     740             : void
     741           0 : Predictor::MaybeCleanupOldDBFiles()
     742             : {
     743           0 :   MOZ_ASSERT(NS_IsMainThread());
     744             : 
     745           0 :   if (!mEnabled || mCleanedUp) {
     746           0 :     return;
     747             :   }
     748             : 
     749           0 :   mCleanedUp = true;
     750             : 
     751             :   // This is used for cleaning up junk left over from the old backend
     752             :   // built on top of sqlite, if necessary.
     753           0 :   nsCOMPtr<nsIFile> dbFile;
     754           0 :   nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
     755           0 :                                        getter_AddRefs(dbFile));
     756           0 :   RETURN_IF_FAILED(rv);
     757           0 :   rv = dbFile->AppendNative(NS_LITERAL_CSTRING("netpredictions.sqlite"));
     758           0 :   RETURN_IF_FAILED(rv);
     759             : 
     760           0 :   nsCOMPtr<nsIThread> ioThread;
     761           0 :   rv = NS_NewNamedThread("NetPredictClean", getter_AddRefs(ioThread));
     762           0 :   RETURN_IF_FAILED(rv);
     763             : 
     764             :   RefPtr<PredictorOldCleanupRunner> runner =
     765           0 :     new PredictorOldCleanupRunner(ioThread, dbFile);
     766           0 :   ioThread->Dispatch(runner, NS_DISPATCH_NORMAL);
     767             : }
     768             : 
     769             : void
     770           0 : Predictor::Shutdown()
     771             : {
     772           0 :   if (!NS_IsMainThread()) {
     773           0 :     MOZ_ASSERT(false, "Predictor::Shutdown called off the main thread!");
     774             :     return;
     775             :   }
     776             : 
     777           0 :   RemoveObserver();
     778             : 
     779           0 :   mInitialized = false;
     780           0 : }
     781             : 
     782             : nsresult
     783           2 : Predictor::Create(nsISupports *aOuter, const nsIID& aIID,
     784             :                   void **aResult)
     785             : {
     786           2 :   MOZ_ASSERT(NS_IsMainThread());
     787             : 
     788             :   nsresult rv;
     789             : 
     790           2 :   if (aOuter != nullptr) {
     791           0 :     return NS_ERROR_NO_AGGREGATION;
     792             :   }
     793             : 
     794           4 :   RefPtr<Predictor> svc = new Predictor();
     795           2 :   if (IsNeckoChild()) {
     796             :     // Child threads only need to be call into the public interface methods
     797             :     // so we don't bother with initialization
     798           1 :     return svc->QueryInterface(aIID, aResult);
     799             :   }
     800             : 
     801           1 :   rv = svc->Init();
     802           1 :   if (NS_FAILED(rv)) {
     803           0 :     PREDICTOR_LOG(("Failed to initialize predictor, predictor will be a noop"));
     804             :   }
     805             : 
     806             :   // We treat init failure the same as the service being disabled, since this
     807             :   // is all an optimization anyway. No need to freak people out. That's why we
     808             :   // gladly continue on QI'ing here.
     809           1 :   rv = svc->QueryInterface(aIID, aResult);
     810             : 
     811           1 :   return rv;
     812             : }
     813             : 
     814             : NS_IMETHODIMP
     815           0 : Predictor::Predict(nsIURI *targetURI, nsIURI *sourceURI,
     816             :                    PredictorPredictReason reason,
     817             :                    JS::HandleValue originAttributes,
     818             :                    nsINetworkPredictorVerifier *verifier,
     819             :                    JSContext* aCx)
     820             : {
     821           0 :   OriginAttributes attrs;
     822             : 
     823           0 :   if (!originAttributes.isObject() ||
     824           0 :       !attrs.Init(aCx, originAttributes)) {
     825           0 :     return NS_ERROR_INVALID_ARG;
     826             :   }
     827             : 
     828           0 :   return PredictNative(targetURI, sourceURI, reason, attrs, verifier);
     829             : }
     830             : 
     831             : // Called from the main thread to initiate predictive actions
     832             : NS_IMETHODIMP
     833           2 : Predictor::PredictNative(nsIURI *targetURI, nsIURI *sourceURI,
     834             :                          PredictorPredictReason reason,
     835             :                          const OriginAttributes& originAttributes,
     836             :                          nsINetworkPredictorVerifier *verifier)
     837             : {
     838           2 :   MOZ_ASSERT(NS_IsMainThread(),
     839             :              "Predictor interface methods must be called on the main thread");
     840             : 
     841           2 :   PREDICTOR_LOG(("Predictor::Predict"));
     842             : 
     843           2 :   if (IsNeckoChild()) {
     844           1 :     MOZ_DIAGNOSTIC_ASSERT(gNeckoChild);
     845             : 
     846           1 :     PREDICTOR_LOG(("    called on child process"));
     847             : 
     848           2 :     ipc::OptionalURIParams serTargetURI, serSourceURI;
     849           1 :     SerializeURI(targetURI, serTargetURI);
     850           1 :     SerializeURI(sourceURI, serSourceURI);
     851             : 
     852             :     // If two different threads are predicting concurently, this will be
     853             :     // overwritten. Thankfully, we only use this in tests, which will
     854             :     // overwrite mVerifier perhaps multiple times for each individual test;
     855             :     // however, within each test, the multiple predict calls should have the
     856             :     // same verifier.
     857           1 :     if (verifier) {
     858           0 :       PREDICTOR_LOG(("    was given a verifier"));
     859           0 :       mChildVerifier = verifier;
     860             :     }
     861           1 :     PREDICTOR_LOG(("    forwarding to parent process"));
     862           1 :     gNeckoChild->SendPredPredict(serTargetURI, serSourceURI,
     863           2 :                                  reason, originAttributes, verifier);
     864           1 :     return NS_OK;
     865             :   }
     866             : 
     867           1 :   PREDICTOR_LOG(("    called on parent process"));
     868             : 
     869           1 :   if (!mInitialized) {
     870           0 :     PREDICTOR_LOG(("    not initialized"));
     871           0 :     return NS_OK;
     872             :   }
     873             : 
     874           1 :   if (!mEnabled) {
     875           0 :     PREDICTOR_LOG(("    not enabled"));
     876           0 :     return NS_OK;
     877             :   }
     878             : 
     879           1 :   if (originAttributes.mPrivateBrowsingId > 0) {
     880             :     // Don't want to do anything in PB mode
     881           0 :     PREDICTOR_LOG(("    in PB mode"));
     882           0 :     return NS_OK;
     883             :   }
     884             : 
     885           1 :   if (!IsNullOrHttp(targetURI) || !IsNullOrHttp(sourceURI)) {
     886             :     // Nothing we can do for non-HTTP[S] schemes
     887           0 :     PREDICTOR_LOG(("    got non-http[s] URI"));
     888           0 :     return NS_OK;
     889             :   }
     890             : 
     891             :   // Ensure we've been given the appropriate arguments for the kind of
     892             :   // prediction we're being asked to do
     893           2 :   nsCOMPtr<nsIURI> uriKey = targetURI;
     894           2 :   nsCOMPtr<nsIURI> originKey;
     895           1 :   switch (reason) {
     896             :     case nsINetworkPredictor::PREDICT_LINK:
     897           0 :       if (!targetURI || !sourceURI) {
     898           0 :         PREDICTOR_LOG(("    link invalid URI state"));
     899           0 :         return NS_ERROR_INVALID_ARG;
     900             :       }
     901             :       // Link hover is a special case where we can predict without hitting the
     902             :       // db, so let's go ahead and fire off that prediction here.
     903             :       PredictForLink(targetURI, sourceURI,
     904           0 :                      originAttributes, verifier);
     905           0 :       return NS_OK;
     906             :     case nsINetworkPredictor::PREDICT_LOAD:
     907           1 :       if (!targetURI || sourceURI) {
     908           0 :         PREDICTOR_LOG(("    load invalid URI state"));
     909           0 :         return NS_ERROR_INVALID_ARG;
     910             :       }
     911           1 :       break;
     912             :     case nsINetworkPredictor::PREDICT_STARTUP:
     913           0 :       if (targetURI || sourceURI) {
     914           0 :         PREDICTOR_LOG(("    startup invalid URI state"));
     915           0 :         return NS_ERROR_INVALID_ARG;
     916             :       }
     917           0 :       uriKey = mStartupURI;
     918           0 :       originKey = mStartupURI;
     919           0 :       break;
     920             :     default:
     921           0 :       PREDICTOR_LOG(("    invalid reason"));
     922           0 :       return NS_ERROR_INVALID_ARG;
     923             :   }
     924             : 
     925             :   Predictor::Reason argReason;
     926           1 :   argReason.mPredict = reason;
     927             : 
     928             :   // First we open the regular cache entry, to ensure we don't gum up the works
     929             :   // waiting on the less-important predictor-only cache entry
     930             :   RefPtr<Predictor::Action> uriAction =
     931             :     new Predictor::Action(Predictor::Action::IS_FULL_URI,
     932             :                           Predictor::Action::DO_PREDICT, argReason, targetURI,
     933           3 :                           nullptr, verifier, this);
     934           2 :   nsAutoCString uriKeyStr;
     935           1 :   uriKey->GetAsciiSpec(uriKeyStr);
     936           1 :   PREDICTOR_LOG(("    Predict uri=%s reason=%d action=%p", uriKeyStr.get(),
     937             :                  reason, uriAction.get()));
     938             : 
     939           2 :   nsCOMPtr<nsICacheStorage> cacheDiskStorage;
     940             : 
     941             :   RefPtr<LoadContextInfo> lci =
     942           3 :     new LoadContextInfo(false, originAttributes);
     943             : 
     944           2 :   nsresult rv = mCacheStorageService->DiskCacheStorage(lci, false,
     945           2 :                                                        getter_AddRefs(cacheDiskStorage));
     946           1 :   NS_ENSURE_SUCCESS(rv, rv);
     947             : 
     948             : 
     949             :   uint32_t openFlags = nsICacheStorage::OPEN_READONLY |
     950             :                        nsICacheStorage::OPEN_SECRETLY |
     951             :                        nsICacheStorage::OPEN_PRIORITY |
     952           1 :                        nsICacheStorage::CHECK_MULTITHREADED;
     953           1 :   cacheDiskStorage->AsyncOpenURI(uriKey, EmptyCString(), openFlags, uriAction);
     954             : 
     955             :   // Now we do the origin-only (and therefore predictor-only) entry
     956           2 :   nsCOMPtr<nsIURI> targetOrigin;
     957           1 :   rv = ExtractOrigin(uriKey, getter_AddRefs(targetOrigin), mIOService);
     958           1 :   NS_ENSURE_SUCCESS(rv, rv);
     959           1 :   if (!originKey) {
     960           1 :     originKey = targetOrigin;
     961             :   }
     962             : 
     963             :   RefPtr<Predictor::Action> originAction =
     964             :     new Predictor::Action(Predictor::Action::IS_ORIGIN,
     965             :                           Predictor::Action::DO_PREDICT, argReason,
     966           3 :                           targetOrigin, nullptr, verifier, this);
     967           2 :   nsAutoCString originKeyStr;
     968           1 :   originKey->GetAsciiSpec(originKeyStr);
     969           1 :   PREDICTOR_LOG(("    Predict origin=%s reason=%d action=%p", originKeyStr.get(),
     970             :                  reason, originAction.get()));
     971           1 :   openFlags = nsICacheStorage::OPEN_READONLY |
     972             :               nsICacheStorage::OPEN_SECRETLY |
     973             :               nsICacheStorage::CHECK_MULTITHREADED;
     974           2 :   cacheDiskStorage->AsyncOpenURI(originKey,
     975           2 :                                  NS_LITERAL_CSTRING(PREDICTOR_ORIGIN_EXTENSION),
     976           3 :                                  openFlags, originAction);
     977             : 
     978           1 :   PREDICTOR_LOG(("    predict returning"));
     979           1 :   return NS_OK;
     980             : }
     981             : 
     982             : bool
     983           1 : Predictor::PredictInternal(PredictorPredictReason reason, nsICacheEntry *entry,
     984             :                            bool isNew, bool fullUri, nsIURI *targetURI,
     985             :                            nsINetworkPredictorVerifier *verifier,
     986             :                            uint8_t stackCount)
     987             : {
     988           1 :   MOZ_ASSERT(NS_IsMainThread());
     989             : 
     990           1 :   PREDICTOR_LOG(("Predictor::PredictInternal"));
     991           1 :   bool rv = false;
     992             : 
     993           2 :   nsCOMPtr<nsILoadContextInfo> lci;
     994           1 :   entry->GetLoadContextInfo(getter_AddRefs(lci));
     995             : 
     996           1 :   if (!lci) {
     997           0 :     return rv;
     998             :   }
     999             : 
    1000           1 :   if (reason == nsINetworkPredictor::PREDICT_LOAD) {
    1001           1 :     MaybeLearnForStartup(targetURI, fullUri,
    1002           2 :                          *lci->OriginAttributesPtr());
    1003             :   }
    1004             : 
    1005           1 :   if (isNew) {
    1006             :     // nothing else we can do here
    1007           0 :     PREDICTOR_LOG(("    new entry"));
    1008           0 :     return rv;
    1009             :   }
    1010             : 
    1011           1 :   switch (reason) {
    1012             :     case nsINetworkPredictor::PREDICT_LOAD:
    1013           1 :       rv = PredictForPageload(entry, targetURI, stackCount, fullUri, verifier);
    1014           1 :       break;
    1015             :     case nsINetworkPredictor::PREDICT_STARTUP:
    1016           0 :       rv = PredictForStartup(entry, fullUri, verifier);
    1017           0 :       break;
    1018             :     default:
    1019           0 :       PREDICTOR_LOG(("    invalid reason"));
    1020           0 :       MOZ_ASSERT(false, "Got unexpected value for prediction reason");
    1021             :   }
    1022             : 
    1023           1 :   return rv;
    1024             : }
    1025             : 
    1026             : void
    1027           0 : Predictor::PredictForLink(nsIURI *targetURI, nsIURI *sourceURI,
    1028             :                           const OriginAttributes& originAttributes,
    1029             :                           nsINetworkPredictorVerifier *verifier)
    1030             : {
    1031           0 :   MOZ_ASSERT(NS_IsMainThread());
    1032             : 
    1033           0 :   PREDICTOR_LOG(("Predictor::PredictForLink"));
    1034           0 :   if (!mSpeculativeService) {
    1035           0 :     PREDICTOR_LOG(("    missing speculative service"));
    1036           0 :     return;
    1037             :   }
    1038             : 
    1039           0 :   if (!mEnableHoverOnSSL) {
    1040           0 :     bool isSSL = false;
    1041           0 :     sourceURI->SchemeIs("https", &isSSL);
    1042           0 :     if (isSSL) {
    1043             :       // We don't want to predict from an HTTPS page, to avoid info leakage
    1044           0 :       PREDICTOR_LOG(("    Not predicting for link hover - on an SSL page"));
    1045           0 :       return;
    1046             :     }
    1047             :   }
    1048             : 
    1049             :   nsCOMPtr<nsIPrincipal> principal =
    1050           0 :     BasePrincipal::CreateCodebasePrincipal(targetURI, originAttributes);
    1051             : 
    1052           0 :   mSpeculativeService->SpeculativeConnect2(targetURI, principal, nullptr);
    1053           0 :   if (verifier) {
    1054           0 :     PREDICTOR_LOG(("    sending verification"));
    1055           0 :     verifier->OnPredictPreconnect(targetURI);
    1056             :   }
    1057             : }
    1058             : 
    1059             : // This is the driver for prediction based on a new pageload.
    1060             : static const uint8_t MAX_PAGELOAD_DEPTH = 10;
    1061             : bool
    1062           1 : Predictor::PredictForPageload(nsICacheEntry *entry, nsIURI *targetURI,
    1063             :                               uint8_t stackCount, bool fullUri,
    1064             :                               nsINetworkPredictorVerifier *verifier)
    1065             : {
    1066           1 :   MOZ_ASSERT(NS_IsMainThread());
    1067             : 
    1068           1 :   PREDICTOR_LOG(("Predictor::PredictForPageload"));
    1069             : 
    1070           1 :   if (stackCount > MAX_PAGELOAD_DEPTH) {
    1071           0 :     PREDICTOR_LOG(("    exceeded recursion depth!"));
    1072           0 :     return false;
    1073             :   }
    1074             : 
    1075             :   uint32_t lastLoad;
    1076           1 :   nsresult rv = entry->GetLastFetched(&lastLoad);
    1077           1 :   NS_ENSURE_SUCCESS(rv, false);
    1078             : 
    1079           1 :   int32_t globalDegradation = CalculateGlobalDegradation(lastLoad);
    1080           1 :   PREDICTOR_LOG(("    globalDegradation = %d", globalDegradation));
    1081             : 
    1082             :   int32_t loadCount;
    1083           1 :   rv = entry->GetFetchCount(&loadCount);
    1084           1 :   NS_ENSURE_SUCCESS(rv, false);
    1085             : 
    1086           2 :   nsCOMPtr<nsILoadContextInfo> lci;
    1087             : 
    1088           1 :   rv = entry->GetLoadContextInfo(getter_AddRefs(lci));
    1089           1 :   NS_ENSURE_SUCCESS(rv, false);
    1090             : 
    1091           2 :   nsCOMPtr<nsIURI> redirectURI;
    1092           2 :   if (WouldRedirect(entry, loadCount, lastLoad, globalDegradation,
    1093           2 :                     getter_AddRefs(redirectURI))) {
    1094           0 :     mPreconnects.AppendElement(redirectURI);
    1095             :     Predictor::Reason reason;
    1096           0 :     reason.mPredict = nsINetworkPredictor::PREDICT_LOAD;
    1097             :     RefPtr<Predictor::Action> redirectAction =
    1098             :       new Predictor::Action(Predictor::Action::IS_FULL_URI,
    1099             :                             Predictor::Action::DO_PREDICT, reason, redirectURI,
    1100           0 :                             nullptr, verifier, this, stackCount + 1);
    1101           0 :     nsAutoCString redirectUriString;
    1102           0 :     redirectURI->GetAsciiSpec(redirectUriString);
    1103             : 
    1104           0 :     nsCOMPtr<nsICacheStorage> cacheDiskStorage;
    1105             : 
    1106           0 :     rv = mCacheStorageService->DiskCacheStorage(lci, false,
    1107           0 :                                                getter_AddRefs(cacheDiskStorage));
    1108           0 :     NS_ENSURE_SUCCESS(rv, false);
    1109             : 
    1110           0 :     PREDICTOR_LOG(("    Predict redirect uri=%s action=%p", redirectUriString.get(),
    1111             :                    redirectAction.get()));
    1112             :     uint32_t openFlags = nsICacheStorage::OPEN_READONLY |
    1113             :                          nsICacheStorage::OPEN_SECRETLY |
    1114             :                          nsICacheStorage::OPEN_PRIORITY |
    1115           0 :                          nsICacheStorage::CHECK_MULTITHREADED;
    1116           0 :     cacheDiskStorage->AsyncOpenURI(redirectURI, EmptyCString(), openFlags,
    1117           0 :                                     redirectAction);
    1118           0 :     return RunPredictions(nullptr, *lci->OriginAttributesPtr(), verifier);
    1119             :   }
    1120             : 
    1121           1 :   CalculatePredictions(entry, targetURI, lastLoad, loadCount, globalDegradation, fullUri);
    1122             : 
    1123           1 :   return RunPredictions(targetURI, *lci->OriginAttributesPtr(), verifier);
    1124             : }
    1125             : 
    1126             : // This is the driver for predicting at browser startup time based on pages that
    1127             : // have previously been loaded close to startup.
    1128             : bool
    1129           0 : Predictor::PredictForStartup(nsICacheEntry *entry, bool fullUri,
    1130             :                              nsINetworkPredictorVerifier *verifier)
    1131             : {
    1132           0 :   MOZ_ASSERT(NS_IsMainThread());
    1133             : 
    1134           0 :   PREDICTOR_LOG(("Predictor::PredictForStartup"));
    1135             : 
    1136           0 :   nsCOMPtr<nsILoadContextInfo> lci;
    1137             : 
    1138           0 :   nsresult rv = entry->GetLoadContextInfo(getter_AddRefs(lci));
    1139           0 :   NS_ENSURE_SUCCESS(rv, false);
    1140             : 
    1141           0 :   int32_t globalDegradation = CalculateGlobalDegradation(mLastStartupTime);
    1142           0 :   CalculatePredictions(entry, nullptr, mLastStartupTime, mStartupCount,
    1143           0 :                        globalDegradation, fullUri);
    1144           0 :   return RunPredictions(nullptr, *lci->OriginAttributesPtr(), verifier);
    1145             : }
    1146             : 
    1147             : // This calculates how much to degrade our confidence in our data based on
    1148             : // the last time this top-level resource was loaded. This "global degradation"
    1149             : // applies to *all* subresources we have associated with the top-level
    1150             : // resource. This will be in addition to any reduction in confidence we have
    1151             : // associated with a particular subresource.
    1152             : int32_t
    1153           1 : Predictor::CalculateGlobalDegradation(uint32_t lastLoad)
    1154             : {
    1155           1 :   MOZ_ASSERT(NS_IsMainThread());
    1156             : 
    1157             :   int32_t globalDegradation;
    1158           1 :   uint32_t delta = NOW_IN_SECONDS() - lastLoad;
    1159           1 :   if (delta < ONE_DAY) {
    1160           1 :     globalDegradation = mPageDegradationDay;
    1161           0 :   } else if (delta < ONE_WEEK) {
    1162           0 :     globalDegradation = mPageDegradationWeek;
    1163           0 :   } else if (delta < ONE_MONTH) {
    1164           0 :     globalDegradation = mPageDegradationMonth;
    1165           0 :   } else if (delta < ONE_YEAR) {
    1166           0 :     globalDegradation = mPageDegradationYear;
    1167             :   } else {
    1168           0 :     globalDegradation = mPageDegradationMax;
    1169             :   }
    1170             : 
    1171           1 :   Telemetry::Accumulate(Telemetry::PREDICTOR_GLOBAL_DEGRADATION,
    1172           1 :                         globalDegradation);
    1173           1 :   return globalDegradation;
    1174             : }
    1175             : 
    1176             : // This calculates our overall confidence that a particular subresource will be
    1177             : // loaded as part of a top-level load.
    1178             : // @param hitCount - the number of times we have loaded this subresource as part
    1179             : //                   of this top-level load
    1180             : // @param hitsPossible - the number of times we have performed this top-level
    1181             : //                       load
    1182             : // @param lastHit - the timestamp of the last time we loaded this subresource as
    1183             : //                  part of this top-level load
    1184             : // @param lastPossible - the timestamp of the last time we performed this
    1185             : //                       top-level load
    1186             : // @param globalDegradation - the degradation for this top-level load as
    1187             : //                            determined by CalculateGlobalDegradation
    1188             : int32_t
    1189           1 : Predictor::CalculateConfidence(uint32_t hitCount, uint32_t hitsPossible,
    1190             :                                uint32_t lastHit, uint32_t lastPossible,
    1191             :                                int32_t globalDegradation)
    1192             : {
    1193           1 :   MOZ_ASSERT(NS_IsMainThread());
    1194             : 
    1195           2 :   Telemetry::AutoCounter<Telemetry::PREDICTOR_PREDICTIONS_CALCULATED> predictionsCalculated;
    1196           1 :   ++predictionsCalculated;
    1197             : 
    1198           1 :   if (!hitsPossible) {
    1199           0 :     return 0;
    1200             :   }
    1201             : 
    1202           1 :   int32_t baseConfidence = (hitCount * 100) / hitsPossible;
    1203           1 :   int32_t maxConfidence = 100;
    1204           1 :   int32_t confidenceDegradation = 0;
    1205             : 
    1206           1 :   if (lastHit < lastPossible) {
    1207             :     // We didn't load this subresource the last time this top-level load was
    1208             :     // performed, so let's not bother preconnecting (at the very least).
    1209           1 :     maxConfidence = mPreconnectMinConfidence - 1;
    1210             : 
    1211             :     // Now calculate how much we want to degrade our confidence based on how
    1212             :     // long it's been between the last time we did this top-level load and the
    1213             :     // last time this top-level load included this subresource.
    1214           1 :     PRTime delta = lastPossible - lastHit;
    1215           1 :     if (delta == 0) {
    1216           0 :       confidenceDegradation = 0;
    1217           1 :     } else if (delta < ONE_DAY) {
    1218           1 :       confidenceDegradation = mSubresourceDegradationDay;
    1219           0 :     } else if (delta < ONE_WEEK) {
    1220           0 :       confidenceDegradation = mSubresourceDegradationWeek;
    1221           0 :     } else if (delta < ONE_MONTH) {
    1222           0 :       confidenceDegradation = mSubresourceDegradationMonth;
    1223           0 :     } else if (delta < ONE_YEAR) {
    1224           0 :       confidenceDegradation = mSubresourceDegradationYear;
    1225             :     } else {
    1226           0 :       confidenceDegradation = mSubresourceDegradationMax;
    1227           0 :       maxConfidence = 0;
    1228             :     }
    1229             :   }
    1230             : 
    1231             :   // Calculate our confidence and clamp it to between 0 and maxConfidence
    1232             :   // (<= 100)
    1233           1 :   int32_t confidence = baseConfidence - confidenceDegradation - globalDegradation;
    1234           1 :   confidence = std::max(confidence, 0);
    1235           1 :   confidence = std::min(confidence, maxConfidence);
    1236             : 
    1237           1 :   Telemetry::Accumulate(Telemetry::PREDICTOR_BASE_CONFIDENCE, baseConfidence);
    1238           1 :   Telemetry::Accumulate(Telemetry::PREDICTOR_SUBRESOURCE_DEGRADATION,
    1239           1 :                         confidenceDegradation);
    1240           1 :   Telemetry::Accumulate(Telemetry::PREDICTOR_CONFIDENCE, confidence);
    1241           1 :   return confidence;
    1242             : }
    1243             : 
    1244             : static void
    1245           4 : MakeMetadataEntry(const uint32_t hitCount, const uint32_t lastHit,
    1246             :                   const uint32_t flags, nsCString &newValue)
    1247             : {
    1248           4 :   newValue.Truncate();
    1249           4 :   newValue.AppendInt(METADATA_VERSION);
    1250           4 :   newValue.Append(',');
    1251           4 :   newValue.AppendInt(hitCount);
    1252           4 :   newValue.Append(',');
    1253           4 :   newValue.AppendInt(lastHit);
    1254           4 :   newValue.Append(',');
    1255           4 :   newValue.AppendInt(flags);
    1256           4 : }
    1257             : 
    1258             : // On every page load, the rolling window gets shifted by one bit, leaving the
    1259             : // lowest bit at 0, to indicate that the subresource in question has not been
    1260             : // seen on the most recent page load. If, at some point later during the page load,
    1261             : // the subresource is seen again, we will then set the lowest bit to 1. This is
    1262             : // how we keep track of how many of the last n pageloads (for n <= 20) a particular
    1263             : // subresource has been seen.
    1264             : // The rolling window is kept in the upper 20 bits of the flags element of the
    1265             : // metadata. This saves 12 bits for regular old flags.
    1266             : void
    1267           0 : Predictor::UpdateRollingLoadCount(nsICacheEntry *entry, const uint32_t flags,
    1268             :                                   const char *key, const uint32_t hitCount,
    1269             :                                   const uint32_t lastHit)
    1270             : {
    1271             :   // Extract just the rolling load count from the flags, shift it to clear the
    1272             :   // lowest bit, and put the new value with the existing flags.
    1273           0 :   uint32_t rollingLoadCount = flags & ~kFlagsMask;
    1274           0 :   rollingLoadCount <<= 1;
    1275           0 :   uint32_t newFlags = (flags & kFlagsMask) | rollingLoadCount;
    1276             : 
    1277             :   // Finally, update the metadata on the cache entry.
    1278           0 :   nsAutoCString newValue;
    1279           0 :   MakeMetadataEntry(hitCount, lastHit, newFlags, newValue);
    1280           0 :   entry->SetMetaDataElement(key, newValue.BeginReading());
    1281           0 : }
    1282             : 
    1283             : void
    1284           1 : Predictor::SanitizePrefs()
    1285             : {
    1286           1 :   if (mPrefetchRollingLoadCount < 0) {
    1287           0 :     mPrefetchRollingLoadCount = 0;
    1288           1 :   } else if (mPrefetchRollingLoadCount > kMaxPrefetchRollingLoadCount) {
    1289           0 :     mPrefetchRollingLoadCount = kMaxPrefetchRollingLoadCount;
    1290             :   }
    1291           1 : }
    1292             : 
    1293             : void
    1294           1 : Predictor::CalculatePredictions(nsICacheEntry *entry, nsIURI *referrer,
    1295             :                                 uint32_t lastLoad, uint32_t loadCount,
    1296             :                                 int32_t globalDegradation, bool fullUri)
    1297             : {
    1298           1 :   MOZ_ASSERT(NS_IsMainThread());
    1299             : 
    1300           1 :   SanitizePrefs();
    1301             : 
    1302             :   // Since the visitor gets called under a cache lock, all we do there is get
    1303             :   // copies of the keys/values we care about, and then do the real work here
    1304           1 :   entry->VisitMetaData(this);
    1305           2 :   nsTArray<nsCString> keysToOperateOn, valuesToOperateOn;
    1306           1 :   keysToOperateOn.SwapElements(mKeysToOperateOn);
    1307           1 :   valuesToOperateOn.SwapElements(mValuesToOperateOn);
    1308             : 
    1309           1 :   MOZ_ASSERT(keysToOperateOn.Length() == valuesToOperateOn.Length());
    1310           2 :   for (size_t i = 0; i < keysToOperateOn.Length(); ++i) {
    1311           1 :     const char *key = keysToOperateOn[i].BeginReading();
    1312           1 :     const char *value = valuesToOperateOn[i].BeginReading();
    1313             : 
    1314           2 :     nsCOMPtr<nsIURI> uri;
    1315             :     uint32_t hitCount, lastHit, flags;
    1316           1 :     if (!ParseMetaDataEntry(key, value, getter_AddRefs(uri), hitCount, lastHit, flags)) {
    1317             :       // This failed, get rid of it so we don't waste space
    1318           0 :       entry->SetMetaDataElement(key, nullptr);
    1319           0 :       continue;
    1320             :     }
    1321             : 
    1322           1 :     int32_t confidence = CalculateConfidence(hitCount, loadCount, lastHit,
    1323           1 :                                              lastLoad, globalDegradation);
    1324           1 :     if (fullUri) {
    1325           0 :       UpdateRollingLoadCount(entry, flags, key, hitCount, lastHit);
    1326             :     }
    1327           1 :     PREDICTOR_LOG(("CalculatePredictions key=%s value=%s confidence=%d", key, value, confidence));
    1328           1 :     if (!fullUri) {
    1329             :       // Not full URI - don't prefetch! No sense in it!
    1330           1 :       PREDICTOR_LOG(("    forcing non-cacheability - not full URI"));
    1331           1 :       flags &= ~FLAG_PREFETCHABLE;
    1332           0 :     } else if (!referrer) {
    1333             :       // No referrer means we can't prefetch, so pretend it's non-cacheable,
    1334             :       // no matter what.
    1335           0 :       PREDICTOR_LOG(("    forcing non-cacheability - no referrer"));
    1336           0 :       flags &= ~FLAG_PREFETCHABLE;
    1337             :     } else {
    1338           0 :       uint32_t expectedRollingLoadCount = (1 << mPrefetchRollingLoadCount) - 1;
    1339           0 :       expectedRollingLoadCount <<= kRollingLoadOffset;
    1340           0 :       if ((flags & expectedRollingLoadCount) != expectedRollingLoadCount) {
    1341           0 :         PREDICTOR_LOG(("    forcing non-cacheability - missed a load"));
    1342           0 :         flags &= ~FLAG_PREFETCHABLE;
    1343             :       }
    1344             :     }
    1345             : 
    1346           1 :     PREDICTOR_LOG(("    setting up prediction"));
    1347           1 :     SetupPrediction(confidence, flags, uri);
    1348             :   }
    1349           1 : }
    1350             : 
    1351             : // (Maybe) adds a predictive action to the prediction runner, based on our
    1352             : // calculated confidence for the subresource in question.
    1353             : void
    1354           1 : Predictor::SetupPrediction(int32_t confidence, uint32_t flags, nsIURI *uri)
    1355             : {
    1356           1 :   MOZ_ASSERT(NS_IsMainThread());
    1357             : 
    1358           2 :   nsAutoCString uriStr;
    1359           1 :   uri->GetAsciiSpec(uriStr);
    1360           1 :   PREDICTOR_LOG(("SetupPrediction mEnablePrefetch=%d mPrefetchMinConfidence=%d "
    1361             :                  "mPreconnectMinConfidence=%d mPreresolveMinConfidence=%d "
    1362             :                  "flags=%d confidence=%d uri=%s", mEnablePrefetch,
    1363             :                  mPrefetchMinConfidence, mPreconnectMinConfidence,
    1364             :                  mPreresolveMinConfidence, flags, confidence, uriStr.get()));
    1365           1 :   if (mEnablePrefetch && (flags & FLAG_PREFETCHABLE) &&
    1366           0 :       (mPrefetchRollingLoadCount || (confidence >= mPrefetchMinConfidence))) {
    1367           0 :     mPrefetches.AppendElement(uri);
    1368           1 :   } else if (confidence >= mPreconnectMinConfidence) {
    1369           0 :     mPreconnects.AppendElement(uri);
    1370           1 :   } else if (confidence >= mPreresolveMinConfidence) {
    1371           0 :     mPreresolves.AppendElement(uri);
    1372             :   }
    1373           1 : }
    1374             : 
    1375             : nsresult
    1376           0 : Predictor::Prefetch(nsIURI *uri, nsIURI *referrer,
    1377             :                     const OriginAttributes& originAttributes,
    1378             :                     nsINetworkPredictorVerifier *verifier)
    1379             : {
    1380           0 :   nsAutoCString strUri, strReferrer;
    1381           0 :   uri->GetAsciiSpec(strUri);
    1382           0 :   referrer->GetAsciiSpec(strReferrer);
    1383           0 :   PREDICTOR_LOG(("Predictor::Prefetch uri=%s referrer=%s verifier=%p",
    1384             :                  strUri.get(), strReferrer.get(), verifier));
    1385           0 :   nsCOMPtr<nsIChannel> channel;
    1386           0 :   nsresult rv = NS_NewChannel(getter_AddRefs(channel), uri,
    1387             :                               nsContentUtils::GetSystemPrincipal(),
    1388             :                               nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
    1389             :                               nsIContentPolicy::TYPE_OTHER,
    1390             :                               nullptr, /* aLoadGroup */
    1391             :                               nullptr, /* aCallbacks */
    1392           0 :                               nsIRequest::LOAD_BACKGROUND);
    1393             : 
    1394           0 :   if (NS_FAILED(rv)) {
    1395           0 :     PREDICTOR_LOG(("    NS_NewChannel failed rv=0x%" PRIX32, static_cast<uint32_t>(rv)));
    1396           0 :     return rv;
    1397             :   }
    1398             : 
    1399           0 :   nsCOMPtr<nsILoadInfo> loadInfo = channel->GetLoadInfo();
    1400           0 :   if (loadInfo) {
    1401           0 :     rv = loadInfo->SetOriginAttributes(originAttributes);
    1402             :   }
    1403             : 
    1404           0 :   if (NS_FAILED(rv)) {
    1405           0 :     PREDICTOR_LOG(("    Set originAttributes into loadInfo failed rv=0x%" PRIX32,
    1406             :                    static_cast<uint32_t>(rv)));
    1407           0 :     return rv;
    1408             :   }
    1409             : 
    1410           0 :   nsCOMPtr<nsIHttpChannel> httpChannel;
    1411           0 :   httpChannel = do_QueryInterface(channel);
    1412           0 :   if (!httpChannel) {
    1413           0 :     PREDICTOR_LOG(("    Could not get HTTP Channel from new channel!"));
    1414           0 :     return NS_ERROR_UNEXPECTED;
    1415             :   }
    1416             : 
    1417           0 :   rv = httpChannel->SetReferrer(referrer);
    1418           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1419             :   // XXX - set a header here to indicate this is a prefetch?
    1420             : 
    1421             :   nsCOMPtr<nsIStreamListener> listener = new PrefetchListener(verifier, uri,
    1422           0 :                                                               this);
    1423           0 :   PREDICTOR_LOG(("    calling AsyncOpen2 listener=%p channel=%p", listener.get(),
    1424             :                  channel.get()));
    1425           0 :   rv = channel->AsyncOpen2(listener);
    1426           0 :   if (NS_FAILED(rv)) {
    1427           0 :     PREDICTOR_LOG(("    AsyncOpen2 failed rv=0x%" PRIX32, static_cast<uint32_t>(rv)));
    1428             :   }
    1429             : 
    1430           0 :   return rv;
    1431             : }
    1432             : 
    1433             : // Runs predictions that have been set up.
    1434             : bool
    1435           1 : Predictor::RunPredictions(nsIURI *referrer,
    1436             :                           const OriginAttributes& originAttributes,
    1437             :                           nsINetworkPredictorVerifier *verifier)
    1438             : {
    1439           1 :   MOZ_ASSERT(NS_IsMainThread(), "Running prediction off main thread");
    1440             : 
    1441           1 :   PREDICTOR_LOG(("Predictor::RunPredictions"));
    1442             : 
    1443           1 :   bool predicted = false;
    1444             :   uint32_t len, i;
    1445             : 
    1446           2 :   nsTArray<nsCOMPtr<nsIURI>> prefetches, preconnects, preresolves;
    1447           1 :   prefetches.SwapElements(mPrefetches);
    1448           1 :   preconnects.SwapElements(mPreconnects);
    1449           1 :   preresolves.SwapElements(mPreresolves);
    1450             : 
    1451           2 :   Telemetry::AutoCounter<Telemetry::PREDICTOR_TOTAL_PREDICTIONS> totalPredictions;
    1452           2 :   Telemetry::AutoCounter<Telemetry::PREDICTOR_TOTAL_PREFETCHES> totalPrefetches;
    1453           2 :   Telemetry::AutoCounter<Telemetry::PREDICTOR_TOTAL_PRECONNECTS> totalPreconnects;
    1454           2 :   Telemetry::AutoCounter<Telemetry::PREDICTOR_TOTAL_PRERESOLVES> totalPreresolves;
    1455             : 
    1456           1 :   len = prefetches.Length();
    1457           1 :   for (i = 0; i < len; ++i) {
    1458           0 :     PREDICTOR_LOG(("    doing prefetch"));
    1459           0 :     nsCOMPtr<nsIURI> uri = prefetches[i];
    1460           0 :     if (NS_SUCCEEDED(Prefetch(uri, referrer,
    1461             :                               originAttributes, verifier))) {
    1462           0 :       ++totalPredictions;
    1463           0 :       ++totalPrefetches;
    1464           0 :       predicted = true;
    1465             :     }
    1466             :   }
    1467             : 
    1468           1 :   len = preconnects.Length();
    1469           1 :   for (i = 0; i < len; ++i) {
    1470           0 :     PREDICTOR_LOG(("    doing preconnect"));
    1471           0 :     nsCOMPtr<nsIURI> uri = preconnects[i];
    1472           0 :     ++totalPredictions;
    1473           0 :     ++totalPreconnects;
    1474             :     nsCOMPtr<nsIPrincipal> principal =
    1475           0 :       BasePrincipal::CreateCodebasePrincipal(uri, originAttributes);
    1476           0 :     mSpeculativeService->SpeculativeConnect2(uri, principal, this);
    1477           0 :     predicted = true;
    1478           0 :     if (verifier) {
    1479           0 :       PREDICTOR_LOG(("    sending preconnect verification"));
    1480           0 :       verifier->OnPredictPreconnect(uri);
    1481             :     }
    1482             :   }
    1483             : 
    1484           1 :   len = preresolves.Length();
    1485           1 :   for (i = 0; i < len; ++i) {
    1486           0 :     nsCOMPtr<nsIURI> uri = preresolves[i];
    1487           0 :     ++totalPredictions;
    1488           0 :     ++totalPreresolves;
    1489           0 :     nsAutoCString hostname;
    1490           0 :     uri->GetAsciiHost(hostname);
    1491           0 :     PREDICTOR_LOG(("    doing preresolve %s", hostname.get()));
    1492           0 :     nsCOMPtr<nsICancelable> tmpCancelable;
    1493           0 :     mDnsService->AsyncResolveNative(hostname,
    1494             :                                     (nsIDNSService::RESOLVE_PRIORITY_MEDIUM |
    1495             :                                      nsIDNSService::RESOLVE_SPECULATE),
    1496             :                                     mDNSListener, nullptr, originAttributes,
    1497           0 :                                     getter_AddRefs(tmpCancelable));
    1498           0 :     predicted = true;
    1499           0 :     if (verifier) {
    1500           0 :       PREDICTOR_LOG(("    sending preresolve verification"));
    1501           0 :       verifier->OnPredictDNS(uri);
    1502             :     }
    1503             :   }
    1504             : 
    1505           2 :   return predicted;
    1506             : }
    1507             : 
    1508             : // Find out if a top-level page is likely to redirect.
    1509             : bool
    1510           1 : Predictor::WouldRedirect(nsICacheEntry *entry, uint32_t loadCount,
    1511             :                          uint32_t lastLoad, int32_t globalDegradation,
    1512             :                          nsIURI **redirectURI)
    1513             : {
    1514             :   // TODO - not doing redirects for first go around
    1515           1 :   MOZ_ASSERT(NS_IsMainThread());
    1516             : 
    1517           1 :   return false;
    1518             : }
    1519             : 
    1520             : NS_IMETHODIMP
    1521           0 : Predictor::Learn(nsIURI *targetURI, nsIURI *sourceURI,
    1522             :                  PredictorLearnReason reason,
    1523             :                  JS::HandleValue originAttributes,
    1524             :                  JSContext* aCx)
    1525             : {
    1526           0 :   OriginAttributes attrs;
    1527             : 
    1528           0 :   if (!originAttributes.isObject() ||
    1529           0 :       !attrs.Init(aCx, originAttributes)) {
    1530           0 :     return NS_ERROR_INVALID_ARG;
    1531             :   }
    1532             : 
    1533           0 :   return LearnNative(targetURI, sourceURI, reason, attrs);
    1534             : }
    1535             : 
    1536             : // Called from the main thread to update the database
    1537             : NS_IMETHODIMP
    1538           6 : Predictor::LearnNative(nsIURI *targetURI, nsIURI *sourceURI,
    1539             :                        PredictorLearnReason reason,
    1540             :                        const OriginAttributes& originAttributes)
    1541             : {
    1542           6 :   MOZ_ASSERT(NS_IsMainThread(),
    1543             :              "Predictor interface methods must be called on the main thread");
    1544             : 
    1545           6 :   PREDICTOR_LOG(("Predictor::Learn"));
    1546             : 
    1547           6 :   if (IsNeckoChild()) {
    1548           3 :     MOZ_DIAGNOSTIC_ASSERT(gNeckoChild);
    1549             : 
    1550           3 :     PREDICTOR_LOG(("    called on child process"));
    1551             : 
    1552             :     RefPtr<PredictorLearnRunnable> runnable = new PredictorLearnRunnable(
    1553           6 :       targetURI, sourceURI, reason, originAttributes);
    1554           6 :     SystemGroup::Dispatch("PredictorLearnRunnable",
    1555             :                           TaskCategory::Other,
    1556           9 :                           runnable.forget());
    1557             : 
    1558           3 :     return NS_OK;
    1559             :   }
    1560             : 
    1561           3 :   PREDICTOR_LOG(("    called on parent process"));
    1562             : 
    1563           3 :   if (!mInitialized) {
    1564           0 :     PREDICTOR_LOG(("    not initialized"));
    1565           0 :     return NS_OK;
    1566             :   }
    1567             : 
    1568           3 :   if (!mEnabled) {
    1569           0 :     PREDICTOR_LOG(("    not enabled"));
    1570           0 :     return NS_OK;
    1571             :   }
    1572             : 
    1573           3 :   if (originAttributes.mPrivateBrowsingId > 0) {
    1574             :     // Don't want to do anything in PB mode
    1575           0 :     PREDICTOR_LOG(("    in PB mode"));
    1576           0 :     return NS_OK;
    1577             :   }
    1578             : 
    1579           3 :   if (!IsNullOrHttp(targetURI) || !IsNullOrHttp(sourceURI)) {
    1580           0 :     PREDICTOR_LOG(("    got non-HTTP[S] URI"));
    1581           0 :     return NS_ERROR_INVALID_ARG;
    1582             :   }
    1583             : 
    1584           6 :   nsCOMPtr<nsIURI> targetOrigin;
    1585           6 :   nsCOMPtr<nsIURI> sourceOrigin;
    1586           6 :   nsCOMPtr<nsIURI> uriKey;
    1587           6 :   nsCOMPtr<nsIURI> originKey;
    1588             :   nsresult rv;
    1589             : 
    1590           3 :   switch (reason) {
    1591             :   case nsINetworkPredictor::LEARN_LOAD_TOPLEVEL:
    1592           1 :     if (!targetURI || sourceURI) {
    1593           0 :       PREDICTOR_LOG(("    load toplevel invalid URI state"));
    1594           0 :       return NS_ERROR_INVALID_ARG;
    1595             :     }
    1596           1 :     rv = ExtractOrigin(targetURI, getter_AddRefs(targetOrigin), mIOService);
    1597           1 :     NS_ENSURE_SUCCESS(rv, rv);
    1598           1 :     uriKey = targetURI;
    1599           1 :     originKey = targetOrigin;
    1600           1 :     break;
    1601             :   case nsINetworkPredictor::LEARN_STARTUP:
    1602           0 :     if (!targetURI || sourceURI) {
    1603           0 :       PREDICTOR_LOG(("    startup invalid URI state"));
    1604           0 :       return NS_ERROR_INVALID_ARG;
    1605             :     }
    1606           0 :     rv = ExtractOrigin(targetURI, getter_AddRefs(targetOrigin), mIOService);
    1607           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1608           0 :     uriKey = mStartupURI;
    1609           0 :     originKey = mStartupURI;
    1610           0 :     break;
    1611             :   case nsINetworkPredictor::LEARN_LOAD_REDIRECT:
    1612             :   case nsINetworkPredictor::LEARN_LOAD_SUBRESOURCE:
    1613           2 :     if (!targetURI || !sourceURI) {
    1614           0 :       PREDICTOR_LOG(("    redirect/subresource invalid URI state"));
    1615           0 :       return NS_ERROR_INVALID_ARG;
    1616             :     }
    1617           2 :     rv = ExtractOrigin(targetURI, getter_AddRefs(targetOrigin), mIOService);
    1618           2 :     NS_ENSURE_SUCCESS(rv, rv);
    1619           2 :     rv = ExtractOrigin(sourceURI, getter_AddRefs(sourceOrigin), mIOService);
    1620           2 :     NS_ENSURE_SUCCESS(rv, rv);
    1621           2 :     uriKey = sourceURI;
    1622           2 :     originKey = sourceOrigin;
    1623           2 :     break;
    1624             :   default:
    1625           0 :     PREDICTOR_LOG(("    invalid reason"));
    1626           0 :     return NS_ERROR_INVALID_ARG;
    1627             :   }
    1628             : 
    1629           6 :   Telemetry::AutoCounter<Telemetry::PREDICTOR_LEARN_ATTEMPTS> learnAttempts;
    1630           3 :   ++learnAttempts;
    1631             : 
    1632             :   Predictor::Reason argReason;
    1633           3 :   argReason.mLearn = reason;
    1634             : 
    1635             :   // We always open the full uri (general cache) entry first, so we don't gum up
    1636             :   // the works waiting on predictor-only entries to open
    1637             :   RefPtr<Predictor::Action> uriAction =
    1638             :     new Predictor::Action(Predictor::Action::IS_FULL_URI,
    1639             :                           Predictor::Action::DO_LEARN, argReason, targetURI,
    1640           9 :                           sourceURI, nullptr, this);
    1641           6 :   nsAutoCString uriKeyStr, targetUriStr, sourceUriStr;
    1642           3 :   uriKey->GetAsciiSpec(uriKeyStr);
    1643           3 :   targetURI->GetAsciiSpec(targetUriStr);
    1644           3 :   if (sourceURI) {
    1645           2 :     sourceURI->GetAsciiSpec(sourceUriStr);
    1646             :   }
    1647           3 :   PREDICTOR_LOG(("    Learn uriKey=%s targetURI=%s sourceURI=%s reason=%d "
    1648             :                  "action=%p", uriKeyStr.get(), targetUriStr.get(),
    1649             :                  sourceUriStr.get(), reason, uriAction.get()));
    1650             : 
    1651           6 :   nsCOMPtr<nsICacheStorage> cacheDiskStorage;
    1652             : 
    1653             :   RefPtr<LoadContextInfo> lci =
    1654           9 :     new LoadContextInfo(false, originAttributes);
    1655             : 
    1656           6 :   rv = mCacheStorageService->DiskCacheStorage(lci, false,
    1657           6 :                                               getter_AddRefs(cacheDiskStorage));
    1658           3 :   NS_ENSURE_SUCCESS(rv, rv);
    1659             : 
    1660             :   // For learning full URI things, we *always* open readonly and secretly, as we
    1661             :   // rely on actual pageloads to update the entry's metadata for us.
    1662             :   uint32_t uriOpenFlags = nsICacheStorage::OPEN_READONLY |
    1663             :                           nsICacheStorage::OPEN_SECRETLY |
    1664           3 :                           nsICacheStorage::CHECK_MULTITHREADED;
    1665           3 :   if (reason == nsINetworkPredictor::LEARN_LOAD_TOPLEVEL) {
    1666             :     // Learning for toplevel we want to open the full uri entry priority, since
    1667             :     // it's likely this entry will be used soon anyway, and we want this to be
    1668             :     // opened ASAP.
    1669           1 :     uriOpenFlags |= nsICacheStorage::OPEN_PRIORITY;
    1670             :   }
    1671           6 :   cacheDiskStorage->AsyncOpenURI(uriKey, EmptyCString(), uriOpenFlags,
    1672           6 :                                   uriAction);
    1673             : 
    1674             :   // Now we open the origin-only (and therefore predictor-only) entry
    1675             :   RefPtr<Predictor::Action> originAction =
    1676             :     new Predictor::Action(Predictor::Action::IS_ORIGIN,
    1677             :                           Predictor::Action::DO_LEARN, argReason, targetOrigin,
    1678           9 :                           sourceOrigin, nullptr, this);
    1679           6 :   nsAutoCString originKeyStr, targetOriginStr, sourceOriginStr;
    1680           3 :   originKey->GetAsciiSpec(originKeyStr);
    1681           3 :   targetOrigin->GetAsciiSpec(targetOriginStr);
    1682           3 :   if (sourceOrigin) {
    1683           2 :     sourceOrigin->GetAsciiSpec(sourceOriginStr);
    1684             :   }
    1685           3 :   PREDICTOR_LOG(("    Learn originKey=%s targetOrigin=%s sourceOrigin=%s reason=%d "
    1686             :                  "action=%p", originKeyStr.get(), targetOriginStr.get(),
    1687             :                  sourceOriginStr.get(), reason, originAction.get()));
    1688             :   uint32_t originOpenFlags;
    1689           3 :   if (reason == nsINetworkPredictor::LEARN_LOAD_TOPLEVEL) {
    1690             :     // This is the only case when we want to update the 'last used' metadata on
    1691             :     // the cache entry we're getting. This only applies to predictor-specific
    1692             :     // entries.
    1693           1 :     originOpenFlags = nsICacheStorage::OPEN_NORMALLY |
    1694             :                       nsICacheStorage::CHECK_MULTITHREADED;
    1695             :   } else {
    1696           2 :     originOpenFlags = nsICacheStorage::OPEN_READONLY |
    1697             :                       nsICacheStorage::OPEN_SECRETLY |
    1698             :                       nsICacheStorage::CHECK_MULTITHREADED;
    1699             :   }
    1700           6 :   cacheDiskStorage->AsyncOpenURI(originKey,
    1701           6 :                                   NS_LITERAL_CSTRING(PREDICTOR_ORIGIN_EXTENSION),
    1702           9 :                                   originOpenFlags, originAction);
    1703             : 
    1704           3 :   PREDICTOR_LOG(("Predictor::Learn returning"));
    1705           3 :   return NS_OK;
    1706             : }
    1707             : 
    1708             : void
    1709           5 : Predictor::LearnInternal(PredictorLearnReason reason, nsICacheEntry *entry,
    1710             :                          bool isNew, bool fullUri, nsIURI *targetURI,
    1711             :                          nsIURI *sourceURI)
    1712             : {
    1713           5 :   MOZ_ASSERT(NS_IsMainThread());
    1714             : 
    1715           5 :   PREDICTOR_LOG(("Predictor::LearnInternal"));
    1716             : 
    1717          10 :   nsCString junk;
    1718          16 :   if (!fullUri && reason == nsINetworkPredictor::LEARN_LOAD_TOPLEVEL &&
    1719           8 :       NS_FAILED(entry->GetMetaDataElement(SEEN_META_DATA, getter_Copies(junk)))) {
    1720             :     // This is an origin-only entry that we haven't seen before. Let's mark it
    1721             :     // as seen.
    1722           0 :     PREDICTOR_LOG(("    marking new origin entry as seen"));
    1723           0 :     nsresult rv = entry->SetMetaDataElement(SEEN_META_DATA, "1");
    1724           0 :     if (NS_FAILED(rv)) {
    1725           0 :       PREDICTOR_LOG(("    failed to mark origin entry seen"));
    1726           0 :       return;
    1727             :     }
    1728             : 
    1729             :     // Need to ensure someone else can get to the entry if necessary
    1730           0 :     entry->MetaDataReady();
    1731           0 :     return;
    1732             :   }
    1733             : 
    1734           5 :   switch (reason) {
    1735             :     case nsINetworkPredictor::LEARN_LOAD_TOPLEVEL:
    1736             :       // This case only exists to be used during tests - code outside the
    1737             :       // predictor tests should NEVER call Learn with LEARN_LOAD_TOPLEVEL.
    1738             :       // The predictor xpcshell test needs this branch, however, because we
    1739             :       // have no real page loads in xpcshell, and this is how we fake it up
    1740             :       // so that all the work that normally happens behind the scenes in a
    1741             :       // page load can be done for testing purposes.
    1742           1 :       if (fullUri && mDoingTests) {
    1743           0 :         PREDICTOR_LOG(("    WARNING - updating rolling load count. "
    1744             :                        "If you see this outside tests, you did it wrong"));
    1745           0 :         SanitizePrefs();
    1746             : 
    1747             :         // Since the visitor gets called under a cache lock, all we do there is get
    1748             :         // copies of the keys/values we care about, and then do the real work here
    1749           0 :         entry->VisitMetaData(this);
    1750           0 :         nsTArray<nsCString> keysToOperateOn, valuesToOperateOn;
    1751           0 :         keysToOperateOn.SwapElements(mKeysToOperateOn);
    1752           0 :         valuesToOperateOn.SwapElements(mValuesToOperateOn);
    1753             : 
    1754           0 :         MOZ_ASSERT(keysToOperateOn.Length() == valuesToOperateOn.Length());
    1755           0 :         for (size_t i = 0; i < keysToOperateOn.Length(); ++i) {
    1756           0 :           const char *key = keysToOperateOn[i].BeginReading();
    1757           0 :           const char *value = valuesToOperateOn[i].BeginReading();
    1758             : 
    1759           0 :           nsCOMPtr<nsIURI> uri;
    1760             :           uint32_t hitCount, lastHit, flags;
    1761           0 :           if (!ParseMetaDataEntry(nullptr, value, nullptr, hitCount, lastHit, flags)) {
    1762             :             // This failed, get rid of it so we don't waste space
    1763           0 :             entry->SetMetaDataElement(key, nullptr);
    1764           0 :             continue;
    1765             :           }
    1766           0 :           UpdateRollingLoadCount(entry, flags, key, hitCount, lastHit);
    1767           0 :         }
    1768             :       } else {
    1769           1 :         PREDICTOR_LOG(("    nothing to do for toplevel"));
    1770             :       }
    1771           1 :       break;
    1772             :     case nsINetworkPredictor::LEARN_LOAD_REDIRECT:
    1773           0 :       if (fullUri) {
    1774           0 :         LearnForRedirect(entry, targetURI);
    1775             :       }
    1776           0 :       break;
    1777             :     case nsINetworkPredictor::LEARN_LOAD_SUBRESOURCE:
    1778           4 :       LearnForSubresource(entry, targetURI);
    1779           4 :       break;
    1780             :     case nsINetworkPredictor::LEARN_STARTUP:
    1781           0 :       LearnForStartup(entry, targetURI);
    1782           0 :       break;
    1783             :     default:
    1784           0 :       PREDICTOR_LOG(("    unexpected reason value"));
    1785           0 :       MOZ_ASSERT(false, "Got unexpected value for learn reason!");
    1786             :   }
    1787             : }
    1788             : 
    1789           0 : NS_IMPL_ISUPPORTS(Predictor::SpaceCleaner, nsICacheEntryMetaDataVisitor)
    1790             : 
    1791             : NS_IMETHODIMP
    1792           0 : Predictor::SpaceCleaner::OnMetaDataElement(const char *key, const char *value)
    1793             : {
    1794           0 :   MOZ_ASSERT(NS_IsMainThread());
    1795             : 
    1796           0 :   if (!IsURIMetadataElement(key)) {
    1797             :     // This isn't a bit of metadata we care about
    1798           0 :     return NS_OK;
    1799             :   }
    1800             : 
    1801             :   uint32_t hitCount, lastHit, flags;
    1802           0 :   bool ok = mPredictor->ParseMetaDataEntry(nullptr, value, nullptr,
    1803           0 :                                            hitCount, lastHit, flags);
    1804             : 
    1805           0 :   if (!ok) {
    1806             :     // Couldn't parse this one, just get rid of it
    1807           0 :     nsCString nsKey;
    1808           0 :     nsKey.AssignASCII(key);
    1809           0 :     mLongKeysToDelete.AppendElement(nsKey);
    1810           0 :     return NS_OK;
    1811             :   }
    1812             : 
    1813           0 :   nsCString uri(key + (sizeof(META_DATA_PREFIX) - 1));
    1814           0 :   uint32_t uriLength = uri.Length();
    1815           0 :   if (uriLength > mPredictor->mMaxURILength) {
    1816             :     // Default to getting rid of URIs that are too long and were put in before
    1817             :     // we had our limit on URI length, in order to free up some space.
    1818           0 :     nsCString nsKey;
    1819           0 :     nsKey.AssignASCII(key);
    1820           0 :     mLongKeysToDelete.AppendElement(nsKey);
    1821           0 :     return NS_OK;
    1822             :   }
    1823             : 
    1824           0 :   if (!mLRUKeyToDelete || lastHit < mLRUStamp) {
    1825           0 :     mLRUKeyToDelete = key;
    1826           0 :     mLRUStamp = lastHit;
    1827             :   }
    1828             : 
    1829           0 :   return NS_OK;
    1830             : }
    1831             : 
    1832             : void
    1833           0 : Predictor::SpaceCleaner::Finalize(nsICacheEntry *entry)
    1834             : {
    1835           0 :   MOZ_ASSERT(NS_IsMainThread());
    1836             : 
    1837           0 :   if (mLRUKeyToDelete) {
    1838           0 :     entry->SetMetaDataElement(mLRUKeyToDelete, nullptr);
    1839             :   }
    1840             : 
    1841           0 :   for (size_t i = 0; i < mLongKeysToDelete.Length(); ++i) {
    1842           0 :     entry->SetMetaDataElement(mLongKeysToDelete[i].BeginReading(), nullptr);
    1843             :   }
    1844           0 : }
    1845             : 
    1846             : // Called when a subresource has been hit from a top-level load. Uses the two
    1847             : // helper functions above to update the database appropriately.
    1848             : void
    1849           4 : Predictor::LearnForSubresource(nsICacheEntry *entry, nsIURI *targetURI)
    1850             : {
    1851           4 :   MOZ_ASSERT(NS_IsMainThread());
    1852             : 
    1853           4 :   PREDICTOR_LOG(("Predictor::LearnForSubresource"));
    1854             : 
    1855             :   uint32_t lastLoad;
    1856           4 :   nsresult rv = entry->GetLastFetched(&lastLoad);
    1857           4 :   RETURN_IF_FAILED(rv);
    1858             : 
    1859             :   int32_t loadCount;
    1860           4 :   rv = entry->GetFetchCount(&loadCount);
    1861           4 :   RETURN_IF_FAILED(rv);
    1862             : 
    1863           8 :   nsCString key;
    1864           4 :   key.AssignLiteral(META_DATA_PREFIX);
    1865           8 :   nsCString uri;
    1866           4 :   targetURI->GetAsciiSpec(uri);
    1867           4 :   key.Append(uri);
    1868           4 :   if (uri.Length() > mMaxURILength) {
    1869             :     // We do this to conserve space/prevent OOMs
    1870           0 :     PREDICTOR_LOG(("    uri too long!"));
    1871           0 :     entry->SetMetaDataElement(key.BeginReading(), nullptr);
    1872           0 :     return;
    1873             :   }
    1874             : 
    1875           8 :   nsCString value;
    1876           4 :   rv = entry->GetMetaDataElement(key.BeginReading(), getter_Copies(value));
    1877             : 
    1878             :   uint32_t hitCount, lastHit, flags;
    1879           6 :   bool isNewResource = (NS_FAILED(rv) ||
    1880           2 :                         !ParseMetaDataEntry(nullptr, value.BeginReading(),
    1881           4 :                                             nullptr, hitCount, lastHit, flags));
    1882             : 
    1883           4 :   int32_t resourceCount = 0;
    1884           4 :   if (isNewResource) {
    1885             :     // This is a new addition
    1886           2 :     PREDICTOR_LOG(("    new resource"));
    1887           4 :     nsCString s;
    1888           2 :     rv = entry->GetMetaDataElement(RESOURCE_META_DATA, getter_Copies(s));
    1889           2 :     if (NS_SUCCEEDED(rv)) {
    1890           1 :       resourceCount = atoi(s.BeginReading());
    1891             :     }
    1892           2 :     if (resourceCount >= mMaxResourcesPerEntry) {
    1893             :       RefPtr<Predictor::SpaceCleaner> cleaner =
    1894           0 :         new Predictor::SpaceCleaner(this);
    1895           0 :       entry->VisitMetaData(cleaner);
    1896           0 :       cleaner->Finalize(entry);
    1897             :     } else {
    1898           2 :       ++resourceCount;
    1899             :     }
    1900           4 :     nsAutoCString count;
    1901           2 :     count.AppendInt(resourceCount);
    1902           2 :     rv = entry->SetMetaDataElement(RESOURCE_META_DATA, count.BeginReading());
    1903           2 :     if (NS_FAILED(rv)) {
    1904           0 :       PREDICTOR_LOG(("    failed to update resource count"));
    1905           0 :       return;
    1906             :     }
    1907           2 :     hitCount = 1;
    1908           2 :     flags = 0;
    1909             :   } else {
    1910           2 :     PREDICTOR_LOG(("    existing resource"));
    1911           2 :     hitCount = std::min(hitCount + 1, static_cast<uint32_t>(loadCount));
    1912             :   }
    1913             : 
    1914             :   // Update the rolling load count to mark this sub-resource as seen on the
    1915             :   // most-recent pageload so it can be eligible for prefetch (assuming all
    1916             :   // the other stars align).
    1917           4 :   flags |= (1 << kRollingLoadOffset);
    1918             : 
    1919           8 :   nsCString newValue;
    1920           4 :   MakeMetadataEntry(hitCount, lastLoad, flags, newValue);
    1921           4 :   rv = entry->SetMetaDataElement(key.BeginReading(), newValue.BeginReading());
    1922           4 :   PREDICTOR_LOG(("    SetMetaDataElement -> 0x%08" PRIX32, static_cast<uint32_t>(rv)));
    1923           4 :   if (NS_FAILED(rv) && isNewResource) {
    1924             :     // Roll back the increment to the resource count we made above.
    1925           0 :     PREDICTOR_LOG(("    rolling back resource count update"));
    1926           0 :     --resourceCount;
    1927           0 :     if (resourceCount == 0) {
    1928           0 :       entry->SetMetaDataElement(RESOURCE_META_DATA, nullptr);
    1929             :     } else {
    1930           0 :       nsAutoCString count;
    1931           0 :       count.AppendInt(resourceCount);
    1932           0 :       entry->SetMetaDataElement(RESOURCE_META_DATA, count.BeginReading());
    1933             :     }
    1934             :   }
    1935             : }
    1936             : 
    1937             : // This is called when a top-level loaded ended up redirecting to a different
    1938             : // URI so we can keep track of that fact.
    1939             : void
    1940           0 : Predictor::LearnForRedirect(nsICacheEntry *entry, nsIURI *targetURI)
    1941             : {
    1942           0 :   MOZ_ASSERT(NS_IsMainThread());
    1943             : 
    1944             :   // TODO - not doing redirects for first go around
    1945           0 :   PREDICTOR_LOG(("Predictor::LearnForRedirect"));
    1946           0 : }
    1947             : 
    1948             : // This will add a page to our list of startup pages if it's being loaded
    1949             : // before our startup window has expired.
    1950             : void
    1951           1 : Predictor::MaybeLearnForStartup(nsIURI *uri, bool fullUri,
    1952             :                                 const OriginAttributes& originAttributes)
    1953             : {
    1954           1 :   MOZ_ASSERT(NS_IsMainThread());
    1955             : 
    1956             :   // TODO - not doing startup for first go around
    1957           1 :   PREDICTOR_LOG(("Predictor::MaybeLearnForStartup"));
    1958           1 : }
    1959             : 
    1960             : // Add information about a top-level load to our list of startup pages
    1961             : void
    1962           0 : Predictor::LearnForStartup(nsICacheEntry *entry, nsIURI *targetURI)
    1963             : {
    1964           0 :   MOZ_ASSERT(NS_IsMainThread());
    1965             : 
    1966             :   // These actually do the same set of work, just on different entries, so we
    1967             :   // can pass through to get the real work done here
    1968           0 :   PREDICTOR_LOG(("Predictor::LearnForStartup"));
    1969           0 :   LearnForSubresource(entry, targetURI);
    1970           0 : }
    1971             : 
    1972             : bool
    1973           3 : Predictor::ParseMetaDataEntry(const char *key, const char *value, nsIURI **uri,
    1974             :                               uint32_t &hitCount, uint32_t &lastHit,
    1975             :                               uint32_t &flags)
    1976             : {
    1977           3 :   MOZ_ASSERT(NS_IsMainThread());
    1978             : 
    1979           3 :   PREDICTOR_LOG(("Predictor::ParseMetaDataEntry key=%s value=%s",
    1980             :                  key ? key : "", value));
    1981             : 
    1982           3 :   const char *comma = strchr(value, ',');
    1983           3 :   if (!comma) {
    1984           0 :     PREDICTOR_LOG(("    could not find first comma"));
    1985           0 :     return false;
    1986             :   }
    1987             : 
    1988           3 :   uint32_t version = static_cast<uint32_t>(atoi(value));
    1989           3 :   PREDICTOR_LOG(("    version -> %u", version));
    1990             : 
    1991           3 :   if (version != METADATA_VERSION) {
    1992           0 :     PREDICTOR_LOG(("    metadata version mismatch %u != %u", version,
    1993             :                    METADATA_VERSION));
    1994           0 :     return false;
    1995             :   }
    1996             : 
    1997           3 :   value = comma + 1;
    1998           3 :   comma = strchr(value, ',');
    1999           3 :   if (!comma) {
    2000           0 :     PREDICTOR_LOG(("    could not find second comma"));
    2001           0 :     return false;
    2002             :   }
    2003             : 
    2004           3 :   hitCount = static_cast<uint32_t>(atoi(value));
    2005           3 :   PREDICTOR_LOG(("    hitCount -> %u", hitCount));
    2006             : 
    2007           3 :   value = comma + 1;
    2008           3 :   comma = strchr(value, ',');
    2009           3 :   if (!comma) {
    2010           0 :     PREDICTOR_LOG(("    could not find third comma"));
    2011           0 :     return false;
    2012             :   }
    2013             : 
    2014           3 :   lastHit = static_cast<uint32_t>(atoi(value));
    2015           3 :   PREDICTOR_LOG(("    lastHit -> %u", lastHit));
    2016             : 
    2017           3 :   value = comma + 1;
    2018           3 :   flags = static_cast<uint32_t>(atoi(value));
    2019           3 :   PREDICTOR_LOG(("    flags -> %u", flags));
    2020             : 
    2021           3 :   if (key) {
    2022           1 :     const char *uriStart = key + (sizeof(META_DATA_PREFIX) - 1);
    2023           1 :     nsresult rv = NS_NewURI(uri, uriStart, nullptr, mIOService);
    2024           1 :     if (NS_FAILED(rv)) {
    2025           0 :       PREDICTOR_LOG(("    NS_NewURI returned 0x%" PRIX32, static_cast<uint32_t>(rv)));
    2026           0 :       return false;
    2027             :     }
    2028           1 :     PREDICTOR_LOG(("    uri -> %s", uriStart));
    2029             :   }
    2030             : 
    2031           3 :   return true;
    2032             : }
    2033             : 
    2034             : NS_IMETHODIMP
    2035           0 : Predictor::Reset()
    2036             : {
    2037           0 :   MOZ_ASSERT(NS_IsMainThread(),
    2038             :              "Predictor interface methods must be called on the main thread");
    2039             : 
    2040           0 :   PREDICTOR_LOG(("Predictor::Reset"));
    2041             : 
    2042           0 :   if (IsNeckoChild()) {
    2043           0 :     MOZ_DIAGNOSTIC_ASSERT(gNeckoChild);
    2044             : 
    2045           0 :     PREDICTOR_LOG(("    forwarding to parent process"));
    2046           0 :     gNeckoChild->SendPredReset();
    2047           0 :     return NS_OK;
    2048             :   }
    2049             : 
    2050           0 :   PREDICTOR_LOG(("    called on parent process"));
    2051             : 
    2052           0 :   if (!mInitialized) {
    2053           0 :     PREDICTOR_LOG(("    not initialized"));
    2054           0 :     return NS_OK;
    2055             :   }
    2056             : 
    2057           0 :   if (!mEnabled) {
    2058           0 :     PREDICTOR_LOG(("    not enabled"));
    2059           0 :     return NS_OK;
    2060             :   }
    2061             : 
    2062           0 :   RefPtr<Predictor::Resetter> reset = new Predictor::Resetter(this);
    2063           0 :   PREDICTOR_LOG(("    created a resetter"));
    2064           0 :   mCacheStorageService->AsyncVisitAllStorages(reset, true);
    2065           0 :   PREDICTOR_LOG(("    Cache async launched, returning now"));
    2066             : 
    2067           0 :   return NS_OK;
    2068             : }
    2069             : 
    2070           0 : NS_IMPL_ISUPPORTS(Predictor::Resetter,
    2071             :                   nsICacheEntryOpenCallback,
    2072             :                   nsICacheEntryMetaDataVisitor,
    2073             :                   nsICacheStorageVisitor);
    2074             : 
    2075           0 : Predictor::Resetter::Resetter(Predictor *predictor)
    2076             :   :mEntriesToVisit(0)
    2077           0 :   ,mPredictor(predictor)
    2078           0 : { }
    2079             : 
    2080             : NS_IMETHODIMP
    2081           0 : Predictor::Resetter::OnCacheEntryCheck(nsICacheEntry *entry,
    2082             :                                        nsIApplicationCache *appCache,
    2083             :                                        uint32_t *result)
    2084             : {
    2085           0 :   *result = nsICacheEntryOpenCallback::ENTRY_WANTED;
    2086           0 :   return NS_OK;
    2087             : }
    2088             : 
    2089             : NS_IMETHODIMP
    2090           0 : Predictor::Resetter::OnCacheEntryAvailable(nsICacheEntry *entry, bool isNew,
    2091             :                                            nsIApplicationCache *appCache,
    2092             :                                            nsresult result)
    2093             : {
    2094           0 :   MOZ_ASSERT(NS_IsMainThread());
    2095             : 
    2096           0 :   if (NS_FAILED(result)) {
    2097             :     // This can happen when we've tried to open an entry that doesn't exist for
    2098             :     // some non-reset operation, and then get reset shortly thereafter (as
    2099             :     // happens in some of our tests).
    2100           0 :     --mEntriesToVisit;
    2101           0 :     if (!mEntriesToVisit) {
    2102           0 :       Complete();
    2103             :     }
    2104           0 :     return NS_OK;
    2105             :   }
    2106             : 
    2107           0 :   entry->VisitMetaData(this);
    2108           0 :   nsTArray<nsCString> keysToDelete;
    2109           0 :   keysToDelete.SwapElements(mKeysToDelete);
    2110             : 
    2111           0 :   for (size_t i = 0; i < keysToDelete.Length(); ++i) {
    2112           0 :     const char *key = keysToDelete[i].BeginReading();
    2113           0 :     entry->SetMetaDataElement(key, nullptr);
    2114             :   }
    2115             : 
    2116           0 :   --mEntriesToVisit;
    2117           0 :   if (!mEntriesToVisit) {
    2118           0 :     Complete();
    2119             :   }
    2120             : 
    2121           0 :   return NS_OK;
    2122             : }
    2123             : 
    2124             : NS_IMETHODIMP
    2125           0 : Predictor::Resetter::OnMetaDataElement(const char *asciiKey,
    2126             :                                        const char *asciiValue)
    2127             : {
    2128           0 :   MOZ_ASSERT(NS_IsMainThread());
    2129             : 
    2130           0 :   if (!StringBeginsWith(nsDependentCString(asciiKey),
    2131           0 :                         NS_LITERAL_CSTRING(META_DATA_PREFIX))) {
    2132             :     // Not a metadata entry we care about, carry on
    2133           0 :     return NS_OK;
    2134             :   }
    2135             : 
    2136           0 :   nsCString key;
    2137           0 :   key.AssignASCII(asciiKey);
    2138           0 :   mKeysToDelete.AppendElement(key);
    2139             : 
    2140           0 :   return NS_OK;
    2141             : }
    2142             : 
    2143             : NS_IMETHODIMP
    2144           0 : Predictor::Resetter::OnCacheStorageInfo(uint32_t entryCount, uint64_t consumption,
    2145             :                                         uint64_t capacity, nsIFile *diskDirectory)
    2146             : {
    2147           0 :   MOZ_ASSERT(NS_IsMainThread());
    2148             : 
    2149           0 :   return NS_OK;
    2150             : }
    2151             : 
    2152             : NS_IMETHODIMP
    2153           0 : Predictor::Resetter::OnCacheEntryInfo(nsIURI *uri, const nsACString &idEnhance,
    2154             :                                       int64_t dataSize, int32_t fetchCount,
    2155             :                                       uint32_t lastModifiedTime, uint32_t expirationTime,
    2156             :                                       bool aPinned, nsILoadContextInfo* aInfo)
    2157             : {
    2158           0 :   MOZ_ASSERT(NS_IsMainThread());
    2159             : 
    2160             :   nsresult rv;
    2161             : 
    2162             :   // The predictor will only ever touch entries with no idEnhance ("") or an
    2163             :   // idEnhance of PREDICTOR_ORIGIN_EXTENSION, so we filter out any entries that
    2164             :   // don't match that to avoid doing extra work.
    2165           0 :   if (idEnhance.EqualsLiteral(PREDICTOR_ORIGIN_EXTENSION)) {
    2166             :     // This is an entry we own, so we can just doom it entirely
    2167           0 :     nsCOMPtr<nsICacheStorage> cacheDiskStorage;
    2168             : 
    2169           0 :     rv = mPredictor->mCacheStorageService
    2170           0 :                    ->DiskCacheStorage(aInfo, false,
    2171           0 :                                       getter_AddRefs(cacheDiskStorage));
    2172             : 
    2173           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2174           0 :     cacheDiskStorage->AsyncDoomURI(uri, idEnhance, nullptr);
    2175           0 :   } else if (idEnhance.IsEmpty()) {
    2176             :     // This is an entry we don't own, so we have to be a little more careful and
    2177             :     // just get rid of our own metadata entries. Append it to an array of things
    2178             :     // to operate on and then do the operations later so we don't end up calling
    2179             :     // Complete() multiple times/too soon.
    2180           0 :     ++mEntriesToVisit;
    2181           0 :     mURIsToVisit.AppendElement(uri);
    2182           0 :     mInfosToVisit.AppendElement(aInfo);
    2183             :   }
    2184             : 
    2185           0 :   return NS_OK;
    2186             : }
    2187             : 
    2188             : NS_IMETHODIMP
    2189           0 : Predictor::Resetter::OnCacheEntryVisitCompleted()
    2190             : {
    2191           0 :   MOZ_ASSERT(NS_IsMainThread());
    2192             : 
    2193             :   nsresult rv;
    2194             : 
    2195           0 :   nsTArray<nsCOMPtr<nsIURI>> urisToVisit;
    2196           0 :   urisToVisit.SwapElements(mURIsToVisit);
    2197             : 
    2198           0 :   MOZ_ASSERT(mEntriesToVisit == urisToVisit.Length());
    2199             : 
    2200           0 :   nsTArray<nsCOMPtr<nsILoadContextInfo>> infosToVisit;
    2201           0 :   infosToVisit.SwapElements(mInfosToVisit);
    2202             : 
    2203           0 :   MOZ_ASSERT(mEntriesToVisit == infosToVisit.Length());
    2204             : 
    2205           0 :   if (!mEntriesToVisit) {
    2206           0 :     Complete();
    2207           0 :     return NS_OK;
    2208             :   }
    2209             : 
    2210           0 :   uint32_t entriesToVisit = urisToVisit.Length();
    2211           0 :   for (uint32_t i = 0; i < entriesToVisit; ++i) {
    2212           0 :     nsCString u;
    2213           0 :     nsCOMPtr<nsICacheStorage> cacheDiskStorage;
    2214             : 
    2215           0 :     rv = mPredictor->mCacheStorageService
    2216           0 :                    ->DiskCacheStorage(infosToVisit[i], false,
    2217           0 :                                       getter_AddRefs(cacheDiskStorage));
    2218           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2219             : 
    2220             : 
    2221           0 :     urisToVisit[i]->GetAsciiSpec(u);
    2222           0 :     cacheDiskStorage->AsyncOpenURI(
    2223           0 :         urisToVisit[i], EmptyCString(),
    2224             :         nsICacheStorage::OPEN_READONLY | nsICacheStorage::OPEN_SECRETLY | nsICacheStorage::CHECK_MULTITHREADED,
    2225           0 :         this);
    2226             :   }
    2227             : 
    2228           0 :   return NS_OK;
    2229             : }
    2230             : 
    2231             : void
    2232           0 : Predictor::Resetter::Complete()
    2233             : {
    2234           0 :   MOZ_ASSERT(NS_IsMainThread());
    2235             : 
    2236           0 :   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
    2237           0 :   if (!obs) {
    2238           0 :     PREDICTOR_LOG(("COULD NOT GET OBSERVER SERVICE!"));
    2239           0 :     return;
    2240             :   }
    2241             : 
    2242           0 :   obs->NotifyObservers(nullptr, "predictor-reset-complete", nullptr);
    2243             : }
    2244             : 
    2245             : // Helper functions to make using the predictor easier from native code
    2246             : 
    2247           3 : static StaticRefPtr<nsINetworkPredictor> sPredictor;
    2248             : 
    2249             : static nsresult
    2250           4 : EnsureGlobalPredictor(nsINetworkPredictor **aPredictor)
    2251             : {
    2252           4 :   MOZ_ASSERT(NS_IsMainThread());
    2253             : 
    2254           4 :   if (!sPredictor) {
    2255             :     nsresult rv;
    2256             :     nsCOMPtr<nsINetworkPredictor> predictor =
    2257           2 :       do_GetService("@mozilla.org/network/predictor;1",
    2258           2 :                     &rv);
    2259           1 :     NS_ENSURE_SUCCESS(rv, rv);
    2260           1 :     sPredictor = predictor;
    2261           1 :     ClearOnShutdown(&sPredictor);
    2262             :   }
    2263             : 
    2264           8 :   nsCOMPtr<nsINetworkPredictor> predictor = sPredictor.get();
    2265           4 :   predictor.forget(aPredictor);
    2266           4 :   return NS_OK;
    2267             : }
    2268             : 
    2269             : nsresult
    2270           6 : PredictorPredict(nsIURI *targetURI, nsIURI *sourceURI,
    2271             :                  PredictorPredictReason reason,
    2272             :                  const OriginAttributes& originAttributes,
    2273             :                  nsINetworkPredictorVerifier *verifier)
    2274             : {
    2275           6 :   MOZ_ASSERT(NS_IsMainThread());
    2276             : 
    2277           6 :   if (!IsNullOrHttp(targetURI) || !IsNullOrHttp(sourceURI)) {
    2278           5 :     return NS_OK;
    2279             :   }
    2280             : 
    2281           2 :   nsCOMPtr<nsINetworkPredictor> predictor;
    2282           1 :   nsresult rv = EnsureGlobalPredictor(getter_AddRefs(predictor));
    2283           1 :   NS_ENSURE_SUCCESS(rv, rv);
    2284             : 
    2285           1 :   return predictor->PredictNative(targetURI, sourceURI, reason,
    2286           1 :                                   originAttributes, verifier);
    2287             : }
    2288             : 
    2289             : nsresult
    2290          10 : PredictorLearn(nsIURI *targetURI, nsIURI *sourceURI,
    2291             :                PredictorLearnReason reason,
    2292             :                const OriginAttributes& originAttributes)
    2293             : {
    2294          10 :   MOZ_ASSERT(NS_IsMainThread());
    2295             : 
    2296          10 :   if (!IsNullOrHttp(targetURI) || !IsNullOrHttp(sourceURI)) {
    2297           7 :     return NS_OK;
    2298             :   }
    2299             : 
    2300           6 :   nsCOMPtr<nsINetworkPredictor> predictor;
    2301           3 :   nsresult rv = EnsureGlobalPredictor(getter_AddRefs(predictor));
    2302           3 :   NS_ENSURE_SUCCESS(rv, rv);
    2303             : 
    2304           3 :   return predictor->LearnNative(targetURI, sourceURI, reason, originAttributes);
    2305             : }
    2306             : 
    2307             : nsresult
    2308          41 : PredictorLearn(nsIURI *targetURI, nsIURI *sourceURI,
    2309             :                PredictorLearnReason reason,
    2310             :                nsILoadGroup *loadGroup)
    2311             : {
    2312          41 :   MOZ_ASSERT(NS_IsMainThread());
    2313             : 
    2314          41 :   if (!IsNullOrHttp(targetURI) || !IsNullOrHttp(sourceURI)) {
    2315          41 :     return NS_OK;
    2316             :   }
    2317             : 
    2318           0 :   nsCOMPtr<nsINetworkPredictor> predictor;
    2319           0 :   nsresult rv = EnsureGlobalPredictor(getter_AddRefs(predictor));
    2320           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2321             : 
    2322           0 :   nsCOMPtr<nsILoadContext> loadContext;
    2323           0 :   OriginAttributes originAttributes;
    2324             : 
    2325           0 :   if (loadGroup) {
    2326           0 :     nsCOMPtr<nsIInterfaceRequestor> callbacks;
    2327           0 :     loadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
    2328           0 :     if (callbacks) {
    2329           0 :       loadContext = do_GetInterface(callbacks);
    2330             : 
    2331           0 :       if (loadContext) {
    2332           0 :         loadContext->GetOriginAttributes(originAttributes);
    2333             :       }
    2334             :     }
    2335             :   }
    2336             : 
    2337           0 :   return predictor->LearnNative(targetURI, sourceURI, reason, originAttributes);
    2338             : }
    2339             : 
    2340             : nsresult
    2341          27 : PredictorLearn(nsIURI *targetURI, nsIURI *sourceURI,
    2342             :                PredictorLearnReason reason,
    2343             :                nsIDocument *document)
    2344             : {
    2345          27 :   MOZ_ASSERT(NS_IsMainThread());
    2346             : 
    2347          27 :   if (!IsNullOrHttp(targetURI) || !IsNullOrHttp(sourceURI)) {
    2348          27 :     return NS_OK;
    2349             :   }
    2350             : 
    2351           0 :   nsCOMPtr<nsINetworkPredictor> predictor;
    2352           0 :   nsresult rv = EnsureGlobalPredictor(getter_AddRefs(predictor));
    2353           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2354             : 
    2355           0 :   OriginAttributes originAttributes;
    2356             : 
    2357           0 :   if (document) {
    2358           0 :     nsCOMPtr<nsIPrincipal> docPrincipal = document->NodePrincipal();
    2359             : 
    2360           0 :     if (docPrincipal) {
    2361           0 :       originAttributes = docPrincipal->OriginAttributesRef();
    2362             :     }
    2363             :   }
    2364             : 
    2365           0 :   return predictor->LearnNative(targetURI, sourceURI, reason, originAttributes);
    2366             : }
    2367             : 
    2368             : nsresult
    2369           3 : PredictorLearnRedirect(nsIURI *targetURI, nsIChannel *channel,
    2370             :                        const OriginAttributes& originAttributes)
    2371             : {
    2372           3 :   MOZ_ASSERT(NS_IsMainThread());
    2373             : 
    2374           6 :   nsCOMPtr<nsIURI> sourceURI;
    2375           3 :   nsresult rv = channel->GetOriginalURI(getter_AddRefs(sourceURI));
    2376           3 :   NS_ENSURE_SUCCESS(rv, rv);
    2377             : 
    2378             :   bool sameUri;
    2379           3 :   rv = targetURI->Equals(sourceURI, &sameUri);
    2380           3 :   NS_ENSURE_SUCCESS(rv, rv);
    2381             : 
    2382           3 :   if (sameUri) {
    2383           2 :     return NS_OK;
    2384             :   }
    2385             : 
    2386           1 :   if (!IsNullOrHttp(targetURI) || !IsNullOrHttp(sourceURI)) {
    2387           1 :     return NS_OK;
    2388             :   }
    2389             : 
    2390           0 :   nsCOMPtr<nsINetworkPredictor> predictor;
    2391           0 :   rv = EnsureGlobalPredictor(getter_AddRefs(predictor));
    2392           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2393             : 
    2394           0 :   return predictor->LearnNative(targetURI, sourceURI,
    2395             :                                 nsINetworkPredictor::LEARN_LOAD_REDIRECT,
    2396           0 :                                 originAttributes);
    2397             : }
    2398             : 
    2399             : // nsINetworkPredictorVerifier
    2400             : 
    2401             : /**
    2402             :  * Call through to the child's verifier (only during tests)
    2403             :  */
    2404             : NS_IMETHODIMP
    2405           0 : Predictor::OnPredictPrefetch(nsIURI *aURI, uint32_t httpStatus)
    2406             : {
    2407           0 :   if (IsNeckoChild()) {
    2408           0 :     if (mChildVerifier) {
    2409             :       // Ideally, we'd assert here. But since we're slowly moving towards a
    2410             :       // world where we have multiple child processes, and only one child process
    2411             :       // will be likely to have a verifier, we have to play it safer.
    2412           0 :       return mChildVerifier->OnPredictPrefetch(aURI, httpStatus);
    2413             :     }
    2414           0 :     return NS_OK;
    2415             :   }
    2416             : 
    2417           0 :   ipc::URIParams serURI;
    2418           0 :   SerializeURI(aURI, serURI);
    2419             : 
    2420           0 :   for (auto* cp : ContentParent::AllProcesses(ContentParent::eLive)) {
    2421           0 :     PNeckoParent* neckoParent = SingleManagedOrNull(cp->ManagedPNeckoParent());
    2422           0 :     if (!neckoParent) {
    2423           0 :       continue;
    2424             :     }
    2425           0 :     if (!neckoParent->SendPredOnPredictPrefetch(serURI, httpStatus)) {
    2426           0 :       return NS_ERROR_NOT_AVAILABLE;
    2427             :     }
    2428             :   }
    2429             : 
    2430           0 :   return NS_OK;
    2431             : }
    2432             : 
    2433             : NS_IMETHODIMP
    2434           0 : Predictor::OnPredictPreconnect(nsIURI *aURI) {
    2435           0 :   if (IsNeckoChild()) {
    2436           0 :     if (mChildVerifier) {
    2437             :       // Ideally, we'd assert here. But since we're slowly moving towards a
    2438             :       // world where we have multiple child processes, and only one child process
    2439             :       // will be likely to have a verifier, we have to play it safer.
    2440           0 :       return mChildVerifier->OnPredictPreconnect(aURI);
    2441             :     }
    2442           0 :     return NS_OK;
    2443             :   }
    2444             : 
    2445           0 :   ipc::URIParams serURI;
    2446           0 :   SerializeURI(aURI, serURI);
    2447             : 
    2448           0 :   for (auto* cp : ContentParent::AllProcesses(ContentParent::eLive)) {
    2449           0 :     PNeckoParent* neckoParent = SingleManagedOrNull(cp->ManagedPNeckoParent());
    2450           0 :     if (!neckoParent) {
    2451           0 :       continue;
    2452             :     }
    2453           0 :     if (!neckoParent->SendPredOnPredictPreconnect(serURI)) {
    2454           0 :       return NS_ERROR_NOT_AVAILABLE;
    2455             :     }
    2456             :   }
    2457             : 
    2458           0 :   return NS_OK;
    2459             : }
    2460             : 
    2461             : NS_IMETHODIMP
    2462           0 : Predictor::OnPredictDNS(nsIURI *aURI) {
    2463           0 :   if (IsNeckoChild()) {
    2464           0 :     if (mChildVerifier) {
    2465             :       // Ideally, we'd assert here. But since we're slowly moving towards a
    2466             :       // world where we have multiple child processes, and only one child process
    2467             :       // will be likely to have a verifier, we have to play it safer.
    2468           0 :       return mChildVerifier->OnPredictDNS(aURI);
    2469             :     }
    2470           0 :     return NS_OK;
    2471             :   }
    2472             : 
    2473           0 :   ipc::URIParams serURI;
    2474           0 :   SerializeURI(aURI, serURI);
    2475             : 
    2476           0 :   for (auto* cp : ContentParent::AllProcesses(ContentParent::eLive)) {
    2477           0 :     PNeckoParent* neckoParent = SingleManagedOrNull(cp->ManagedPNeckoParent());
    2478           0 :     if (!neckoParent) {
    2479           0 :       continue;
    2480             :     }
    2481           0 :     if (!neckoParent->SendPredOnPredictDNS(serURI)) {
    2482           0 :       return NS_ERROR_NOT_AVAILABLE;
    2483             :     }
    2484             :   }
    2485             : 
    2486           0 :   return NS_OK;
    2487             : }
    2488             : 
    2489             : // Predictor::PrefetchListener
    2490             : // nsISupports
    2491           0 : NS_IMPL_ISUPPORTS(Predictor::PrefetchListener,
    2492             :                   nsIStreamListener,
    2493             :                   nsIRequestObserver)
    2494             : 
    2495             : // nsIRequestObserver
    2496             : NS_IMETHODIMP
    2497           0 : Predictor::PrefetchListener::OnStartRequest(nsIRequest *aRequest,
    2498             :                                             nsISupports *aContext)
    2499             : {
    2500           0 :   mStartTime = TimeStamp::Now();
    2501           0 :   return NS_OK;
    2502             : }
    2503             : 
    2504             : NS_IMETHODIMP
    2505           0 : Predictor::PrefetchListener::OnStopRequest(nsIRequest *aRequest,
    2506             :                                            nsISupports *aContext,
    2507             :                                            nsresult aStatusCode)
    2508             : {
    2509           0 :   PREDICTOR_LOG(("OnStopRequest this=%p aStatusCode=0x%" PRIX32,
    2510             :                  this, static_cast<uint32_t>(aStatusCode)));
    2511           0 :   NS_ENSURE_ARG(aRequest);
    2512           0 :   if (NS_FAILED(aStatusCode)) {
    2513           0 :     return aStatusCode;
    2514             :   }
    2515           0 :   Telemetry::AccumulateTimeDelta(Telemetry::PREDICTOR_PREFETCH_TIME, mStartTime);
    2516             : 
    2517           0 :   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest);
    2518           0 :   if (!httpChannel) {
    2519           0 :     PREDICTOR_LOG(("    Could not get HTTP Channel!"));
    2520           0 :     return NS_ERROR_UNEXPECTED;
    2521             :   }
    2522           0 :   nsCOMPtr<nsICachingChannel> cachingChannel = do_QueryInterface(httpChannel);
    2523           0 :   if (!cachingChannel) {
    2524           0 :     PREDICTOR_LOG(("    Could not get caching channel!"));
    2525           0 :     return NS_ERROR_UNEXPECTED;
    2526             :   }
    2527             : 
    2528           0 :   nsresult rv = NS_OK;
    2529             :   uint32_t httpStatus;
    2530           0 :   rv = httpChannel->GetResponseStatus(&httpStatus);
    2531           0 :   if (NS_SUCCEEDED(rv) && httpStatus == 200) {
    2532           0 :     rv = cachingChannel->ForceCacheEntryValidFor(mPredictor->mPrefetchForceValidFor);
    2533           0 :     PREDICTOR_LOG(("    forcing entry valid for %d seconds rv=%" PRIX32,
    2534             :                    mPredictor->mPrefetchForceValidFor, static_cast<uint32_t>(rv)));
    2535             :   } else {
    2536           0 :     rv = cachingChannel->ForceCacheEntryValidFor(0);
    2537           0 :     PREDICTOR_LOG(("    removing any forced validity rv=%" PRIX32,
    2538             :                    static_cast<uint32_t>(rv)));
    2539             :   }
    2540             : 
    2541           0 :   nsAutoCString reqName;
    2542           0 :   rv = aRequest->GetName(reqName);
    2543           0 :   if (NS_FAILED(rv)) {
    2544           0 :     reqName.AssignLiteral("<unknown>");
    2545             :   }
    2546             : 
    2547           0 :   PREDICTOR_LOG(("    request %s status %u", reqName.get(), httpStatus));
    2548             : 
    2549           0 :   if (mVerifier) {
    2550           0 :     mVerifier->OnPredictPrefetch(mURI, httpStatus);
    2551             :   }
    2552             : 
    2553           0 :   return rv;
    2554             : }
    2555             : 
    2556             : // nsIStreamListener
    2557             : NS_IMETHODIMP
    2558           0 : Predictor::PrefetchListener::OnDataAvailable(nsIRequest *aRequest,
    2559             :                                              nsISupports *aContext,
    2560             :                                              nsIInputStream *aInputStream,
    2561             :                                              uint64_t aOffset,
    2562             :                                              const uint32_t aCount)
    2563             : {
    2564             :   uint32_t result;
    2565           0 :   return aInputStream->ReadSegments(NS_DiscardSegment, nullptr, aCount, &result);
    2566             : }
    2567             : 
    2568             : // Miscellaneous Predictor
    2569             : 
    2570             : void
    2571           0 : Predictor::UpdateCacheability(nsIURI *sourceURI, nsIURI *targetURI,
    2572             :                               uint32_t httpStatus,
    2573             :                               nsHttpRequestHead &requestHead,
    2574             :                               nsHttpResponseHead *responseHead,
    2575             :                               nsILoadContextInfo *lci)
    2576             : {
    2577           0 :   MOZ_ASSERT(NS_IsMainThread());
    2578             : 
    2579           0 :   if (lci && lci->IsPrivate()) {
    2580           0 :     PREDICTOR_LOG(("Predictor::UpdateCacheability in PB mode - ignoring"));
    2581           0 :     return;
    2582             :   }
    2583             : 
    2584           0 :   if (!sourceURI || !targetURI) {
    2585           0 :     PREDICTOR_LOG(("Predictor::UpdateCacheability missing source or target uri"));
    2586           0 :     return;
    2587             :   }
    2588             : 
    2589           0 :   if (!IsNullOrHttp(sourceURI) || !IsNullOrHttp(targetURI)) {
    2590           0 :     PREDICTOR_LOG(("Predictor::UpdateCacheability non-http(s) uri"));
    2591           0 :     return;
    2592             :   }
    2593             : 
    2594           0 :   RefPtr<Predictor> self = sSelf;
    2595           0 :   if (self) {
    2596           0 :     nsAutoCString method;
    2597           0 :     requestHead.Method(method);
    2598           0 :     self->UpdateCacheabilityInternal(sourceURI, targetURI, httpStatus,
    2599           0 :                                      method, *lci->OriginAttributesPtr());
    2600             :   }
    2601             : }
    2602             : 
    2603             : void
    2604           0 : Predictor::UpdateCacheabilityInternal(nsIURI *sourceURI, nsIURI *targetURI,
    2605             :                                       uint32_t httpStatus,
    2606             :                                       const nsCString &method,
    2607             :                                       const OriginAttributes& originAttributes)
    2608             : {
    2609           0 :   PREDICTOR_LOG(("Predictor::UpdateCacheability httpStatus=%u", httpStatus));
    2610             : 
    2611             :   nsresult rv;
    2612             : 
    2613           0 :   if (!mInitialized) {
    2614           0 :     PREDICTOR_LOG(("    not initialized"));
    2615           0 :     return;
    2616             :   }
    2617             : 
    2618           0 :   if (!mEnabled) {
    2619           0 :     PREDICTOR_LOG(("    not enabled"));
    2620           0 :     return;
    2621             :   }
    2622             : 
    2623           0 :   if (!mEnablePrefetch) {
    2624           0 :     PREDICTOR_LOG(("    prefetch not enabled"));
    2625           0 :     return;
    2626             :   }
    2627             : 
    2628           0 :   nsCOMPtr<nsICacheStorage> cacheDiskStorage;
    2629             : 
    2630             :   RefPtr<LoadContextInfo> lci =
    2631           0 :     new LoadContextInfo(false, originAttributes);
    2632             : 
    2633           0 :   rv = mCacheStorageService->DiskCacheStorage(lci, false,
    2634           0 :                                              getter_AddRefs(cacheDiskStorage));
    2635           0 :   if (NS_FAILED(rv)) {
    2636           0 :     PREDICTOR_LOG(("    cannot get disk cache storage"));
    2637           0 :     return;
    2638             :   }
    2639             : 
    2640             :   uint32_t openFlags = nsICacheStorage::OPEN_READONLY |
    2641             :                        nsICacheStorage::OPEN_SECRETLY |
    2642           0 :                        nsICacheStorage::CHECK_MULTITHREADED;
    2643             :   RefPtr<Predictor::CacheabilityAction> action =
    2644           0 :     new Predictor::CacheabilityAction(targetURI, httpStatus, method, this);
    2645           0 :   nsAutoCString uri;
    2646           0 :   targetURI->GetAsciiSpec(uri);
    2647           0 :   PREDICTOR_LOG(("    uri=%s action=%p", uri.get(), action.get()));
    2648           0 :   cacheDiskStorage->AsyncOpenURI(sourceURI, EmptyCString(), openFlags, action);
    2649             : }
    2650             : 
    2651           0 : NS_IMPL_ISUPPORTS(Predictor::CacheabilityAction,
    2652             :                   nsICacheEntryOpenCallback,
    2653             :                   nsICacheEntryMetaDataVisitor);
    2654             : 
    2655             : NS_IMETHODIMP
    2656           0 : Predictor::CacheabilityAction::OnCacheEntryCheck(nsICacheEntry *entry,
    2657             :                                                  nsIApplicationCache *appCache,
    2658             :                                                  uint32_t *result)
    2659             : {
    2660           0 :   *result = nsICacheEntryOpenCallback::ENTRY_WANTED;
    2661           0 :   return NS_OK;
    2662             : }
    2663             : 
    2664             : NS_IMETHODIMP
    2665           0 : Predictor::CacheabilityAction::OnCacheEntryAvailable(nsICacheEntry *entry,
    2666             :                                                      bool isNew,
    2667             :                                                      nsIApplicationCache *appCache,
    2668             :                                                      nsresult result)
    2669             : {
    2670           0 :   MOZ_ASSERT(NS_IsMainThread());
    2671             :   // This is being opened read-only, so isNew should always be false
    2672           0 :   MOZ_ASSERT(!isNew);
    2673             : 
    2674           0 :   PREDICTOR_LOG(("CacheabilityAction::OnCacheEntryAvailable this=%p", this));
    2675           0 :   if (NS_FAILED(result)) {
    2676             :     // Nothing to do
    2677           0 :     PREDICTOR_LOG(("    nothing to do result=%" PRIX32 " isNew=%d",
    2678             :                    static_cast<uint32_t>(result), isNew));
    2679           0 :     return NS_OK;
    2680             :   }
    2681             : 
    2682           0 :   nsresult rv = entry->VisitMetaData(this);
    2683           0 :   if (NS_FAILED(rv)) {
    2684           0 :     PREDICTOR_LOG(("    VisitMetaData returned %" PRIx32, static_cast<uint32_t>(rv)));
    2685           0 :     return NS_OK;
    2686             :   }
    2687             : 
    2688           0 :   nsTArray<nsCString> keysToCheck, valuesToCheck;
    2689           0 :   keysToCheck.SwapElements(mKeysToCheck);
    2690           0 :   valuesToCheck.SwapElements(mValuesToCheck);
    2691             : 
    2692           0 :   bool hasQueryString = false;
    2693           0 :   nsAutoCString query;
    2694           0 :   if (NS_SUCCEEDED(mTargetURI->GetQuery(query)) && !query.IsEmpty()) {
    2695           0 :     hasQueryString = true;
    2696             :   }
    2697             : 
    2698           0 :   MOZ_ASSERT(keysToCheck.Length() == valuesToCheck.Length());
    2699           0 :   for (size_t i = 0; i < keysToCheck.Length(); ++i) {
    2700           0 :     const char *key = keysToCheck[i].BeginReading();
    2701           0 :     const char *value = valuesToCheck[i].BeginReading();
    2702           0 :     nsCOMPtr<nsIURI> uri;
    2703             :     uint32_t hitCount, lastHit, flags;
    2704             : 
    2705           0 :     if (!mPredictor->ParseMetaDataEntry(key, value, getter_AddRefs(uri),
    2706             :                                         hitCount, lastHit, flags)) {
    2707           0 :       PREDICTOR_LOG(("    failed to parse key=%s value=%s", key, value));
    2708           0 :       continue;
    2709             :     }
    2710             : 
    2711           0 :     bool eq = false;
    2712           0 :     if (NS_SUCCEEDED(uri->Equals(mTargetURI, &eq)) && eq) {
    2713           0 :       if (mHttpStatus == 200 && mMethod.EqualsLiteral("GET") && !hasQueryString) {
    2714           0 :         PREDICTOR_LOG(("    marking %s cacheable", key));
    2715           0 :         flags |= FLAG_PREFETCHABLE;
    2716             :       } else {
    2717           0 :         PREDICTOR_LOG(("    marking %s uncacheable", key));
    2718           0 :         flags &= ~FLAG_PREFETCHABLE;
    2719             :       }
    2720           0 :       nsCString newValue;
    2721           0 :       MakeMetadataEntry(hitCount, lastHit, flags, newValue);
    2722           0 :       entry->SetMetaDataElement(key, newValue.BeginReading());
    2723           0 :       break;
    2724             :     }
    2725             :   }
    2726             : 
    2727           0 :   return NS_OK;
    2728             : }
    2729             : 
    2730             : NS_IMETHODIMP
    2731           0 : Predictor::CacheabilityAction::OnMetaDataElement(const char *asciiKey,
    2732             :                                                  const char *asciiValue)
    2733             : {
    2734           0 :   MOZ_ASSERT(NS_IsMainThread());
    2735             : 
    2736           0 :   if (!IsURIMetadataElement(asciiKey)) {
    2737           0 :     return NS_OK;
    2738             :   }
    2739             : 
    2740           0 :   nsCString key, value;
    2741           0 :   key.AssignASCII(asciiKey);
    2742           0 :   value.AssignASCII(asciiValue);
    2743           0 :   mKeysToCheck.AppendElement(key);
    2744           0 :   mValuesToCheck.AppendElement(value);
    2745             : 
    2746           0 :   return NS_OK;
    2747             : }
    2748             : 
    2749             : } // namespace net
    2750             : } // namespace mozilla

Generated by: LCOV version 1.13