/* vim: set ts=2 sts=2 et sw=2: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// All these time values are in sec staticconst uint32_t ONE_DAY = 86400U; staticconst uint32_t ONE_WEEK = 7U * ONE_DAY; staticconst uint32_t ONE_MONTH = 30U * ONE_DAY; staticconst uint32_t ONE_YEAR = 365U * ONE_DAY;
// Version of metadata entries we expect staticconst uint32_t METADATA_VERSION = 1;
// Flags available in entries // FLAG_PREFETCHABLE - we have determined that this item is eligible for // prefetch staticconst uint32_t FLAG_PREFETCHABLE = 1 << 0;
// We save 12 bits in the "flags" section of our metadata for actual flags, the // rest are to keep track of a rolling count of which loads a resource has been // used on to determine if we can prefetch that resource or not; staticconst uint8_t kRollingLoadOffset = 12; staticconst int32_t kMaxPrefetchRollingLoadCount = 20; staticconst uint32_t kFlagsMask = ((1 << kRollingLoadOffset) - 1);
// ID Extensions for cache entries #define PREDICTOR_ORIGIN_EXTENSION "predictor-origin"
// Get the full origin (scheme, host, port) out of a URI (maybe should be part // of nsIURI instead?) static nsresult ExtractOrigin(nsIURI* uri, nsIURI** originUri) {
nsAutoCString s;
nsresult rv = nsContentUtils::GetWebExposedOriginSerialization(uri, s);
NS_ENSURE_SUCCESS(rv, rv);
return NS_NewURI(originUri, s);
}
// All URIs we get passed *must* be http or https if they're not null. This // helps ensure that. staticbool IsNullOrHttp(nsIURI* uri) { if (!uri) { returntrue;
}
// Listener for the speculative DNS requests we'll fire off, which just ignores // the result (since we're just trying to warm the cache). This also exists to // reduce round-trips to the main thread, by being something threadsafe the // Predictor can use.
// Class to proxy important information from the initial predictor call through // the cache API and back into the internals of the predictor. We can't use the // predictor itself, as it may have multiple actions in-flight, and each action // has different parameters.
NS_IMPL_ISUPPORTS(Predictor::Action, nsICacheEntryOpenCallback);
namespace { class PredictorLearnRunnable final : public Runnable { public:
PredictorLearnRunnable(nsIURI* targetURI, nsIURI* sourceURI,
PredictorLearnReason reason, const OriginAttributes& oa)
: Runnable("PredictorLearnRunnable"),
mTargetURI(targetURI),
mSourceURI(sourceURI),
mReason(reason),
mOA(oa) {
MOZ_DIAGNOSTIC_ASSERT(targetURI, "Must have a target URI");
}
~PredictorLearnRunnable() = default;
NS_IMETHOD Run() override { if (!gNeckoChild) { // This may have gone away between when this runnable was dispatched and // when it actually runs, so let's be safe here, even though we asserted // earlier.
PREDICTOR_LOG(("predictor::learn (async) gNeckoChild went away")); return NS_OK;
}
PREDICTOR_LOG(("predictor::learn (async) forwarding to parent"));
gNeckoChild->SendPredLearn(mTargetURI, mSourceURI, mReason, mOA);
RefPtr<Predictor> svc = new Predictor(); if (IsNeckoChild()) {
NeckoChild::InitNeckoChild();
// Child threads only need to be call into the public interface methods // so we don't bother with initialization return svc->QueryInterface(aIID, aResult);
}
rv = svc->Init(); if (NS_FAILED(rv)) {
PREDICTOR_LOG(("Failed to initialize predictor, predictor will be a noop"));
}
// We treat init failure the same as the service being disabled, since this // is all an optimization anyway. No need to freak people out. That's why we // gladly continue on QI'ing here.
rv = svc->QueryInterface(aIID, aResult);
// Called from the main thread to initiate predictive actions
NS_IMETHODIMP
Predictor::PredictNative(nsIURI* targetURI, nsIURI* sourceURI,
PredictorPredictReason reason, const OriginAttributes& originAttributes,
nsINetworkPredictorVerifier* verifier) {
MOZ_ASSERT(NS_IsMainThread(), "Predictor interface methods must be called on the main thread");
PREDICTOR_LOG(("Predictor::Predict"));
if (!StaticPrefs::network_predictor_enabled()) {
PREDICTOR_LOG((" not enabled")); return NS_OK;
}
if (IsNeckoChild()) { if (!gNeckoChild) { return NS_ERROR_FAILURE;
}
PREDICTOR_LOG((" called on child process")); // If two different threads are predicting concurently, this will be // overwritten. Thankfully, we only use this in tests, which will // overwrite mVerifier perhaps multiple times for each individual test; // however, within each test, the multiple predict calls should have the // same verifier. if (verifier) {
PREDICTOR_LOG((" was given a verifier"));
mChildVerifier = verifier;
}
PREDICTOR_LOG((" forwarding to parent process"));
gNeckoChild->SendPredPredict(targetURI, sourceURI, reason, originAttributes,
verifier); return NS_OK;
}
PREDICTOR_LOG((" called on parent process"));
if (!mInitialized) {
PREDICTOR_LOG((" not initialized")); return NS_OK;
}
if (originAttributes.IsPrivateBrowsing()) { // Don't want to do anything in PB mode
PREDICTOR_LOG((" in PB mode")); return NS_OK;
}
if (!IsNullOrHttp(targetURI) || !IsNullOrHttp(sourceURI)) { // Nothing we can do for non-HTTP[S] schemes
PREDICTOR_LOG((" got non-http[s] URI")); return NS_OK;
}
// Ensure we've been given the appropriate arguments for the kind of // prediction we're being asked to do
nsCOMPtr<nsIURI> uriKey = targetURI;
nsCOMPtr<nsIURI> originKey; switch (reason) { case nsINetworkPredictor::PREDICT_LINK: if (!targetURI || !sourceURI) {
PREDICTOR_LOG((" link invalid URI state")); return NS_ERROR_INVALID_ARG;
} // Link hover is a special case where we can predict without hitting the // db, so let's go ahead and fire off that prediction here.
PredictForLink(targetURI, sourceURI, originAttributes, verifier); return NS_OK; case nsINetworkPredictor::PREDICT_LOAD: if (!targetURI || sourceURI) {
PREDICTOR_LOG((" load invalid URI state")); return NS_ERROR_INVALID_ARG;
} break; case nsINetworkPredictor::PREDICT_STARTUP: if (targetURI || sourceURI) {
PREDICTOR_LOG((" startup invalid URI state")); return NS_ERROR_INVALID_ARG;
}
uriKey = mStartupURI;
originKey = mStartupURI; break; default:
PREDICTOR_LOG((" invalid reason")); return NS_ERROR_INVALID_ARG;
}
// First we open the regular cache entry, to ensure we don't gum up the works // waiting on the less-important predictor-only cache entry
RefPtr<Predictor::Action> uriAction = new Predictor::Action(
Predictor::Action::IS_FULL_URI, Predictor::Action::DO_PREDICT, argReason,
uriKey, nullptr, verifier, this);
nsAutoCString uriKeyStr;
uriKey->GetAsciiSpec(uriKeyStr);
PREDICTOR_LOG((" Predict uri=%s reason=%d action=%p", uriKeyStr.get(),
reason, uriAction.get()));
nsCOMPtr<nsICacheStorage> cacheDiskStorage;
RefPtr<LoadContextInfo> lci = new LoadContextInfo(false, originAttributes);
// Now we do the origin-only (and therefore predictor-only) entry
nsCOMPtr<nsIURI> targetOrigin;
rv = ExtractOrigin(uriKey, getter_AddRefs(targetOrigin));
NS_ENSURE_SUCCESS(rv, rv); if (!originKey) {
originKey = targetOrigin;
}
PREDICTOR_LOG(("Predictor::PredictForLink")); if (!mSpeculativeService) {
PREDICTOR_LOG((" missing speculative service")); return;
}
if (!StaticPrefs::network_predictor_enable_hover_on_ssl()) { if (sourceURI->SchemeIs("https")) { // We don't want to predict from an HTTPS page, to avoid info leakage
PREDICTOR_LOG((" Not predicting for link hover - on an SSL page")); return;
}
}
nsCOMPtr<nsIPrincipal> principal =
BasePrincipal::CreateContentPrincipal(targetURI, originAttributes);
// This is the driver for prediction based on a new pageload. staticconst uint8_t MAX_PAGELOAD_DEPTH = 10; bool Predictor::PredictForPageload(nsICacheEntry* entry, nsIURI* targetURI,
uint8_t stackCount, bool fullUri,
nsINetworkPredictorVerifier* verifier) {
MOZ_ASSERT(NS_IsMainThread());
// This is the driver for predicting at browser startup time based on pages that // have previously been loaded close to startup. bool Predictor::PredictForStartup(nsICacheEntry* entry, bool fullUri,
nsINetworkPredictorVerifier* verifier) {
MOZ_ASSERT(NS_IsMainThread());
// This calculates how much to degrade our confidence in our data based on // the last time this top-level resource was loaded. This "global degradation" // applies to *all* subresources we have associated with the top-level // resource. This will be in addition to any reduction in confidence we have // associated with a particular subresource.
int32_t Predictor::CalculateGlobalDegradation(uint32_t lastLoad) {
MOZ_ASSERT(NS_IsMainThread());
// This calculates our overall confidence that a particular subresource will be // loaded as part of a top-level load. // @param hitCount - the number of times we have loaded this subresource as part // of this top-level load // @param hitsPossible - the number of times we have performed this top-level // load // @param lastHit - the timestamp of the last time we loaded this subresource as // part of this top-level load // @param lastPossible - the timestamp of the last time we performed this // top-level load // @param globalDegradation - the degradation for this top-level load as // determined by CalculateGlobalDegradation
int32_t Predictor::CalculateConfidence(uint32_t hitCount, uint32_t hitsPossible,
uint32_t lastHit, uint32_t lastPossible,
int32_t globalDegradation) {
MOZ_ASSERT(NS_IsMainThread());
uint32_t predictionsCalculated = 1;
if (!hitsPossible) {
glean::predictor::predictions_calculated.AccumulateSingleSample(
predictionsCalculated); return 0;
}
if (lastHit < lastPossible) { // We didn't load this subresource the last time this top-level load was // performed, so let's not bother preconnecting (at the very least).
maxConfidence =
StaticPrefs::network_predictor_preconnect_min_confidence() - 1;
// Now calculate how much we want to degrade our confidence based on how // long it's been between the last time we did this top-level load and the // last time this top-level load included this subresource.
PRTime delta = lastPossible - lastHit; if (delta == 0) {
confidenceDegradation = 0;
} elseif (delta < ONE_DAY) {
confidenceDegradation =
StaticPrefs::network_predictor_subresource_degradation_day();
} elseif (delta < ONE_WEEK) {
confidenceDegradation =
StaticPrefs::network_predictor_subresource_degradation_week();
} elseif (delta < ONE_MONTH) {
confidenceDegradation =
StaticPrefs::network_predictor_subresource_degradation_month();
} elseif (delta < ONE_YEAR) {
confidenceDegradation =
StaticPrefs::network_predictor_subresource_degradation_year();
} else {
confidenceDegradation =
StaticPrefs::network_predictor_subresource_degradation_max();
maxConfidence = 0;
}
}
// Calculate our confidence and clamp it to between 0 and maxConfidence // (<= 100)
int32_t confidence =
baseConfidence - confidenceDegradation - globalDegradation;
confidence = std::max(confidence, 0);
confidence = std::min(confidence, maxConfidence);
// On every page load, the rolling window gets shifted by one bit, leaving the // lowest bit at 0, to indicate that the subresource in question has not been // seen on the most recent page load. If, at some point later during the page // load, the subresource is seen again, we will then set the lowest bit to 1. // This is how we keep track of how many of the last n pageloads (for n <= 20) a // particular subresource has been seen. The rolling window is kept in the upper // 20 bits of the flags element of the metadata. This saves 12 bits for regular // old flags. void Predictor::UpdateRollingLoadCount(nsICacheEntry* entry, const uint32_t flags, constchar* key, const uint32_t hitCount, const uint32_t lastHit) { // Extract just the rolling load count from the flags, shift it to clear the // lowest bit, and put the new value with the existing flags.
uint32_t rollingLoadCount = flags & ~kFlagsMask;
rollingLoadCount <<= 1;
uint32_t newFlags = (flags & kFlagsMask) | rollingLoadCount;
// Finally, update the metadata on the cache entry.
nsAutoCString newValue;
MakeMetadataEntry(hitCount, lastHit, newFlags, newValue);
entry->SetMetaDataElement(key, newValue.BeginReading());
}
uint32_t Predictor::ClampedPrefetchRollingLoadCount() {
int32_t n = StaticPrefs::network_predictor_prefetch_rolling_load_count(); if (n < 0) { return 0;
} if (n > kMaxPrefetchRollingLoadCount) { return kMaxPrefetchRollingLoadCount;
} return n;
}
// Since the visitor gets called under a cache lock, all we do there is get // copies of the keys/values we care about, and then do the real work here
entry->VisitMetaData(this);
nsTArray<nsCString> keysToOperateOn = std::move(mKeysToOperateOn),
valuesToOperateOn = std::move(mValuesToOperateOn);
MOZ_ASSERT(keysToOperateOn.Length() == valuesToOperateOn.Length()); for (size_t i = 0; i < keysToOperateOn.Length(); ++i) { constchar* key = keysToOperateOn[i].BeginReading(); constchar* value = valuesToOperateOn[i].BeginReading();
nsCString uri;
uint32_t hitCount, lastHit, flags; if (!ParseMetaDataEntry(key, value, uri, hitCount, lastHit, flags)) { // This failed, get rid of it so we don't waste space
entry->SetMetaDataElement(key, nullptr); continue;
}
int32_t confidence = CalculateConfidence(hitCount, loadCount, lastHit,
lastLoad, globalDegradation); if (fullUri) {
UpdateRollingLoadCount(entry, flags, key, hitCount, lastHit);
}
PREDICTOR_LOG(("CalculatePredictions key=%s value=%s confidence=%d", key,
value, confidence));
PrefetchIgnoreReason reason = PREFETCH_OK; if (!fullUri) { // Not full URI - don't prefetch! No sense in it!
PREDICTOR_LOG((" forcing non-cacheability - not full URI")); if (flags & FLAG_PREFETCHABLE) { // This only applies if we had somehow otherwise marked this // prefetchable.
reason = NOT_FULL_URI;
}
flags &= ~FLAG_PREFETCHABLE;
} elseif (!referrer) { // No referrer means we can't prefetch, so pretend it's non-cacheable, // no matter what.
PREDICTOR_LOG((" forcing non-cacheability - no referrer")); if (flags & FLAG_PREFETCHABLE) { // This only applies if we had somehow otherwise marked this // prefetchable.
reason = NO_REFERRER;
}
flags &= ~FLAG_PREFETCHABLE;
} else {
uint32_t expectedRollingLoadCount =
(1 << ClampedPrefetchRollingLoadCount()) - 1;
expectedRollingLoadCount <<= kRollingLoadOffset; if ((flags & expectedRollingLoadCount) != expectedRollingLoadCount) {
PREDICTOR_LOG((" forcing non-cacheability - missed a load")); if (flags & FLAG_PREFETCHABLE) { // This only applies if we had somehow otherwise marked this // prefetchable.
reason = MISSED_A_LOAD;
}
flags &= ~FLAG_PREFETCHABLE;
}
}
PREDICTOR_LOG((" setting up prediction"));
SetupPrediction(confidence, flags, uri, reason);
}
}
// (Maybe) adds a predictive action to the prediction runner, based on our // calculated confidence for the subresource in question. void Predictor::SetupPrediction(int32_t confidence, uint32_t flags, const nsCString& uri,
PrefetchIgnoreReason earlyReason) {
MOZ_ASSERT(NS_IsMainThread());
// prefetchOk == false and reason == PREFETCH_OK indicates that the reason // we aren't prefetching this item is because it was marked un-prefetchable in // our metadata. We already have separate telemetry on that decision, so we // aren't going to accumulate more here. Right now we only care about why // something we had marked prefetchable isn't being prefetched. if (!prefetchOk && reason != PREFETCH_OK) {
glean::predictor::prefetch_ignore_reason.AccumulateSingleSample(reason);
}
if (NS_FAILED(rv)) {
PREDICTOR_LOG(
(" Set originAttributes into loadInfo failed rv=0x%" PRIX32, static_cast<uint32_t>(rv))); return rv;
}
nsCOMPtr<nsIHttpChannel> httpChannel;
httpChannel = do_QueryInterface(channel); if (!httpChannel) {
PREDICTOR_LOG((" Could not get HTTP Channel from new channel!")); return NS_ERROR_UNEXPECTED;
}
nsCOMPtr<nsIReferrerInfo> referrerInfo = new dom::ReferrerInfo(referrer);
rv = httpChannel->SetReferrerInfoWithoutClone(referrerInfo);
NS_ENSURE_SUCCESS(rv, rv); // XXX - set a header here to indicate this is a prefetch?
// Runs predictions that have been set up. bool Predictor::RunPredictions(nsIURI* referrer, const OriginAttributes& originAttributes,
nsINetworkPredictorVerifier* verifier) {
MOZ_ASSERT(NS_IsMainThread(), "Running prediction off main thread");
// Find out if a top-level page is likely to redirect. bool Predictor::WouldRedirect(nsICacheEntry* entry, uint32_t loadCount,
uint32_t lastLoad, int32_t globalDegradation,
nsIURI** redirectURI) { // TODO - not doing redirects for first go around
MOZ_ASSERT(NS_IsMainThread());
// Called from the main thread to update the database
NS_IMETHODIMP
Predictor::LearnNative(nsIURI* targetURI, nsIURI* sourceURI,
PredictorLearnReason reason, const OriginAttributes& originAttributes) {
MOZ_ASSERT(NS_IsMainThread(), "Predictor interface methods must be called on the main thread");
PREDICTOR_LOG(("Predictor::Learn"));
if (!StaticPrefs::network_predictor_enabled()) {
PREDICTOR_LOG((" not enabled")); return NS_OK;
}
if (IsNeckoChild()) { if (!gNeckoChild) { return NS_ERROR_FAILURE;
}
// We always open the full uri (general cache) entry first, so we don't gum up // the works waiting on predictor-only entries to open
RefPtr<Predictor::Action> uriAction = new Predictor::Action(
Predictor::Action::IS_FULL_URI, Predictor::Action::DO_LEARN, argReason,
targetURI, sourceURI, nullptr, this);
nsAutoCString uriKeyStr, targetUriStr, sourceUriStr;
uriKey->GetAsciiSpec(uriKeyStr);
targetURI->GetAsciiSpec(targetUriStr); if (sourceURI) {
sourceURI->GetAsciiSpec(sourceUriStr);
}
PREDICTOR_LOG(
(" Learn uriKey=%s targetURI=%s sourceURI=%s reason=%d " "action=%p",
uriKeyStr.get(), targetUriStr.get(), sourceUriStr.get(), reason,
uriAction.get()));
nsCOMPtr<nsICacheStorage> cacheDiskStorage;
RefPtr<LoadContextInfo> lci = new LoadContextInfo(false, originAttributes);
// For learning full URI things, we *always* open readonly and secretly, as we // rely on actual pageloads to update the entry's metadata for us.
uint32_t uriOpenFlags = nsICacheStorage::OPEN_READONLY |
nsICacheStorage::OPEN_SECRETLY |
nsICacheStorage::CHECK_MULTITHREADED; if (reason == nsINetworkPredictor::LEARN_LOAD_TOPLEVEL) { // Learning for toplevel we want to open the full uri entry priority, since // it's likely this entry will be used soon anyway, and we want this to be // opened ASAP.
uriOpenFlags |= nsICacheStorage::OPEN_PRIORITY;
}
cacheDiskStorage->AsyncOpenURI(uriKey, ""_ns, uriOpenFlags, uriAction);
// Now we open the origin-only (and therefore predictor-only) entry
RefPtr<Predictor::Action> originAction = new Predictor::Action(
Predictor::Action::IS_ORIGIN, Predictor::Action::DO_LEARN, argReason,
targetOrigin, sourceOrigin, nullptr, this);
nsAutoCString originKeyStr, targetOriginStr, sourceOriginStr;
originKey->GetAsciiSpec(originKeyStr);
targetOrigin->GetAsciiSpec(targetOriginStr); if (sourceOrigin) {
sourceOrigin->GetAsciiSpec(sourceOriginStr);
}
PREDICTOR_LOG(
(" Learn originKey=%s targetOrigin=%s sourceOrigin=%s reason=%d " "action=%p",
originKeyStr.get(), targetOriginStr.get(), sourceOriginStr.get(), reason,
originAction.get()));
uint32_t originOpenFlags; if (reason == nsINetworkPredictor::LEARN_LOAD_TOPLEVEL) { // This is the only case when we want to update the 'last used' metadata on // the cache entry we're getting. This only applies to predictor-specific // entries.
originOpenFlags =
nsICacheStorage::OPEN_NORMALLY | nsICacheStorage::CHECK_MULTITHREADED;
} else {
originOpenFlags = nsICacheStorage::OPEN_READONLY |
nsICacheStorage::OPEN_SECRETLY |
nsICacheStorage::CHECK_MULTITHREADED;
}
cacheDiskStorage->AsyncOpenURI(originKey,
nsLiteralCString(PREDICTOR_ORIGIN_EXTENSION),
originOpenFlags, originAction);
nsCString junk; if (!fullUri && reason == nsINetworkPredictor::LEARN_LOAD_TOPLEVEL &&
NS_FAILED(
entry->GetMetaDataElement(SEEN_META_DATA, getter_Copies(junk)))) { // This is an origin-only entry that we haven't seen before. Let's mark it // as seen.
PREDICTOR_LOG((" marking new origin entry as seen"));
nsresult rv = entry->SetMetaDataElement(SEEN_META_DATA, "1"); if (NS_FAILED(rv)) {
PREDICTOR_LOG((" failed to mark origin entry seen")); return;
}
// Need to ensure someone else can get to the entry if necessary
entry->MetaDataReady(); return;
}
switch (reason) { case nsINetworkPredictor::LEARN_LOAD_TOPLEVEL: // This case only exists to be used during tests - code outside the // predictor tests should NEVER call Learn with LEARN_LOAD_TOPLEVEL. // The predictor xpcshell test needs this branch, however, because we // have no real page loads in xpcshell, and this is how we fake it up // so that all the work that normally happens behind the scenes in a // page load can be done for testing purposes. if (fullUri && StaticPrefs::network_predictor_doing_tests()) {
PREDICTOR_LOG(
(" WARNING - updating rolling load count. " "If you see this outside tests, you did it wrong"));
// Since the visitor gets called under a cache lock, all we do there is // get copies of the keys/values we care about, and then do the real // work here
entry->VisitMetaData(this);
nsTArray<nsCString> keysToOperateOn = std::move(mKeysToOperateOn),
valuesToOperateOn = std::move(mValuesToOperateOn);
MOZ_ASSERT(keysToOperateOn.Length() == valuesToOperateOn.Length()); for (size_t i = 0; i < keysToOperateOn.Length(); ++i) { constchar* key = keysToOperateOn[i].BeginReading(); constchar* value = valuesToOperateOn[i].BeginReading();
nsCString uri;
uint32_t hitCount, lastHit, flags; if (!ParseMetaDataEntry(key, value, uri, hitCount, lastHit, flags)) { // This failed, get rid of it so we don't waste space
entry->SetMetaDataElement(key, nullptr); continue;
}
UpdateRollingLoadCount(entry, flags, key, hitCount, lastHit);
}
} else {
PREDICTOR_LOG((" nothing to do for toplevel"));
} break; case nsINetworkPredictor::LEARN_LOAD_REDIRECT: if (fullUri) {
LearnForRedirect(entry, targetURI);
} break; case nsINetworkPredictor::LEARN_LOAD_SUBRESOURCE:
LearnForSubresource(entry, targetURI); break; case nsINetworkPredictor::LEARN_STARTUP:
LearnForStartup(entry, targetURI); break; default:
PREDICTOR_LOG((" unexpected reason value"));
MOZ_ASSERT(false, "Got unexpected value for learn reason!");
}
}
if (!ok) { // Couldn't parse this one, just get rid of it
nsCString nsKey;
nsKey.AssignASCII(key);
mLongKeysToDelete.AppendElement(nsKey); return NS_OK;
}
uint32_t uriLength = uri.Length(); if (uriLength > StaticPrefs::network_predictor_max_uri_length()) { // Default to getting rid of URIs that are too long and were put in before // we had our limit on URI length, in order to free up some space.
nsCString nsKey;
nsKey.AssignASCII(key);
mLongKeysToDelete.AppendElement(nsKey); return NS_OK;
}
if (mLRUKeyToDelete) {
entry->SetMetaDataElement(mLRUKeyToDelete, nullptr);
}
for (size_t i = 0; i < mLongKeysToDelete.Length(); ++i) {
entry->SetMetaDataElement(mLongKeysToDelete[i].BeginReading(), nullptr);
}
}
// Called when a subresource has been hit from a top-level load. Uses the two // helper functions above to update the database appropriately. void Predictor::LearnForSubresource(nsICacheEntry* entry, nsIURI* targetURI) {
MOZ_ASSERT(NS_IsMainThread());
nsCString key;
key.AssignLiteral(META_DATA_PREFIX);
nsCString uri;
targetURI->GetAsciiSpec(uri);
key.Append(uri); if (uri.Length() > StaticPrefs::network_predictor_max_uri_length()) { // We do this to conserve space/prevent OOMs
PREDICTOR_LOG((" uri too long!"));
entry->SetMetaDataElement(key.BeginReading(), nullptr); return;
}
int32_t resourceCount = 0; if (isNewResource) { // This is a new addition
PREDICTOR_LOG((" new resource"));
nsCString s;
rv = entry->GetMetaDataElement(RESOURCE_META_DATA, getter_Copies(s)); if (NS_SUCCEEDED(rv)) {
resourceCount = atoi(s.BeginReading());
} if (resourceCount >=
StaticPrefs::network_predictor_max_resources_per_entry()) {
RefPtr<Predictor::SpaceCleaner> cleaner = new Predictor::SpaceCleaner(this);
entry->VisitMetaData(cleaner);
cleaner->Finalize(entry);
} else {
++resourceCount;
}
nsAutoCString count;
count.AppendInt(resourceCount);
rv = entry->SetMetaDataElement(RESOURCE_META_DATA, count.BeginReading()); if (NS_FAILED(rv)) {
PREDICTOR_LOG((" failed to update resource count")); return;
}
hitCount = 1;
flags = 0;
} else {
PREDICTOR_LOG((" existing resource"));
hitCount = std::min(hitCount + 1, loadCount);
}
// Update the rolling load count to mark this sub-resource as seen on the // most-recent pageload so it can be eligible for prefetch (assuming all // the other stars align).
flags |= (1 << kRollingLoadOffset);
nsCString newValue;
MakeMetadataEntry(hitCount, lastLoad, flags, newValue);
rv = entry->SetMetaDataElement(key.BeginReading(), newValue.BeginReading());
PREDICTOR_LOG(
(" SetMetaDataElement -> 0x%08" PRIX32, static_cast<uint32_t>(rv))); if (NS_FAILED(rv) && isNewResource) { // Roll back the increment to the resource count we made above.
PREDICTOR_LOG((" rolling back resource count update"));
--resourceCount; if (resourceCount == 0) {
entry->SetMetaDataElement(RESOURCE_META_DATA, nullptr);
} else {
nsAutoCString count;
count.AppendInt(resourceCount);
entry->SetMetaDataElement(RESOURCE_META_DATA, count.BeginReading());
}
}
}
// This is called when a top-level loaded ended up redirecting to a different // URI so we can keep track of that fact. void Predictor::LearnForRedirect(nsICacheEntry* entry, nsIURI* targetURI) {
MOZ_ASSERT(NS_IsMainThread());
// TODO - not doing redirects for first go around
PREDICTOR_LOG(("Predictor::LearnForRedirect"));
}
// This will add a page to our list of startup pages if it's being loaded // before our startup window has expired. void Predictor::MaybeLearnForStartup(nsIURI* uri, bool fullUri, const OriginAttributes& originAttributes) {
MOZ_ASSERT(NS_IsMainThread());
// TODO - not doing startup for first go around
PREDICTOR_LOG(("Predictor::MaybeLearnForStartup"));
}
// Add information about a top-level load to our list of startup pages void Predictor::LearnForStartup(nsICacheEntry* entry, nsIURI* targetURI) {
MOZ_ASSERT(NS_IsMainThread());
// These actually do the same set of work, just on different entries, so we // can pass through to get the real work done here
PREDICTOR_LOG(("Predictor::LearnForStartup"));
LearnForSubresource(entry, targetURI);
}
if (NS_FAILED(result)) { // This can happen when we've tried to open an entry that doesn't exist for // some non-reset operation, and then get reset shortly thereafter (as // happens in some of our tests).
--mEntriesToVisit; if (!mEntriesToVisit) {
Complete();
} return NS_OK;
}
if (!StringBeginsWith(nsDependentCString(asciiKey),
nsLiteralCString(META_DATA_PREFIX))) { // Not a metadata entry we care about, carry on return NS_OK;
}
// The predictor will only ever touch entries with no idEnhance ("") or an // idEnhance of PREDICTOR_ORIGIN_EXTENSION, so we filter out any entries that // don't match that to avoid doing extra work. if (idEnhance.EqualsLiteral(PREDICTOR_ORIGIN_EXTENSION)) { // This is an entry we own, so we can just doom it entirely
nsCOMPtr<nsICacheStorage> cacheDiskStorage;
NS_ENSURE_SUCCESS(rv, rv);
cacheDiskStorage->AsyncDoomURI(uri, idEnhance, nullptr);
} elseif (idEnhance.IsEmpty()) { // This is an entry we don't own, so we have to be a little more careful and // just get rid of our own metadata entries. Append it to an array of things // to operate on and then do the operations later so we don't end up calling // Complete() multiple times/too soon.
++mEntriesToVisit;
mURIsToVisit.AppendElement(uri);
mInfosToVisit.AppendElement(aInfo);
}
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); if (!obs) {
PREDICTOR_LOG(("COULD NOT GET OBSERVER SERVICE!")); return;
}
--> --------------------
--> maximum size reached
--> --------------------
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.39Bemerkung:
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.