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