Predictor.cpp 80 KB

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