/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=4 sw=2 sts=2 et cin: */
/* 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/. */
// HttpLog.h should generally be included first
#include "HttpLog.h"
#include "prsystem.h"
#include "AltServiceChild.h"
#include "nsCORSListenerProxy.h"
#include "nsError.h"
#include "nsHttp.h"
#include "nsHttpHandler.h"
#include "nsHttpChannel.h"
#include "nsHTTPCompressConv.h"
#include "nsHttpAuthCache.h"
#include "nsStandardURL.h"
#include "LoadContextInfo.h"
#include "nsCategoryManagerUtils.h"
#include "nsDirectoryServiceDefs.h"
#include "nsSocketProviderService.h"
#include "nsISocketProvider.h"
#include "nsPrintfCString.h"
#include "nsCOMPtr.h"
#include "nsNetCID.h"
#include "mozilla/AppShutdown.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/Components.h"
#include "mozilla/Printf.h"
#include "mozilla/RandomNum.h"
#include "mozilla/SHA1.h"
#include "mozilla/Sprintf.h"
#include "mozilla/StaticPrefs_network.h"
#include "mozilla/StaticPrefs_privacy.h"
#include "mozilla/StoragePrincipalHelper.h"
#include "nsAsyncRedirectVerifyHelper.h"
#include "nsSocketTransportService2.h"
#include "ASpdySession.h"
#include "EventTokenBucket.h"
#include "Tickler.h"
#include "nsIXULAppInfo.h"
#include "nsICookieService.h"
#include "nsIObserverService.h"
#include "nsISiteSecurityService.h"
#include "nsIStreamConverterService.h"
#include "nsCRT.h"
#include "nsIParentalControlsService.h"
#include "nsPIDOMWindow.h"
#include "nsIHttpActivityObserver.h"
#include "nsHttpChannelAuthProvider.h"
#include "nsINetworkLinkService.h"
#include "nsNetUtil.h"
#include "nsServiceManagerUtils.h"
#include "nsComponentManagerUtils.h"
#include "nsSocketTransportService2.h"
#include "nsIOService.h"
#include "nsISupportsPrimitives.h"
#include "nsIXULRuntime.h"
#include "nsCharSeparatedTokenizer.h"
#include "nsRFPService.h"
#include "mozilla/net/rust_helper.h"
#include "mozilla/net/HttpConnectionMgrParent.h"
#include "mozilla/net/NeckoChild.h"
#include "mozilla/net/NeckoParent.h"
#include "mozilla/net/RequestContextService.h"
#include "mozilla/net/SocketProcessParent.h"
#include "mozilla/net/SocketProcessChild.h"
#include "mozilla/ipc/URIUtils.h"
#include "mozilla/glean/NetwerkProtocolHttpMetrics.h"
#include "mozilla/Telemetry.h"
#include "mozilla/Unused.h"
#include "mozilla/AntiTrackingRedirectHeuristic.h"
#include "mozilla/DynamicFpiRedirectHeuristic.h"
#include "mozilla/BasePrincipal.h"
#include "mozilla/LazyIdleThread.h"
#include "mozilla/OriginAttributesHashKey.h"
#include "mozilla/StaticPrefs_image.h"
#include "mozilla/SyncRunnable.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/Navigator.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/network/Connection.h"
#include "nsNSSComponent.h"
#include "TRRServiceChannel.h"
#include <bitset>
#if defined(XP_UNIX)
# include <sys/utsname.h>
#endif
#if defined(MOZ_WIDGET_GTK)
# include
"mozilla/WidgetUtilsGtk.h"
#endif
#if defined(XP_WIN)
# include <windows.h>
# include
"mozilla/WindowsVersion.h"
#endif
#if defined(XP_MACOSX)
# include <CoreServices/CoreServices.h>
#endif
//-----------------------------------------------------------------------------
#include "mozilla/net/HttpChannelChild.h"
#define UA_PREF_PREFIX
"general.useragent."
#ifdef XP_WIN
# define UA_SPARE_PLATFORM
#endif
#define HTTP_PREF_PREFIX
"network.http."
#define INTL_ACCEPT_LANGUAGES
"intl.accept_languages"
#define BROWSER_PREF_PREFIX
"browser.cache."
#define H2MANDATORY_SUITE
"security.ssl3.ecdhe_rsa_aes_128_gcm_sha256"
#define SAFE_HINT_HEADER_VALUE
"safeHint.enabled"
#define SECURITY_PREFIX
"security."
#define DOM_SECURITY_PREFIX
"dom.security"
#define ACCEPT_HEADER_STYLE
"text/css,*/*;q=0.1"
#define ACCEPT_HEADER_ALL
"*/*"
#define UA_PREF(_pref) UA_PREF_PREFIX _pref
#define HTTP_PREF(_pref) HTTP_PREF_PREFIX _pref
#define BROWSER_PREF(_pref) BROWSER_PREF_PREFIX _pref
#define NS_HTTP_PROTOCOL_FLAGS \
(URI_STD | ALLOWS_PROXY | ALLOWS_PROXY_HTTP | URI_LOADABLE_BY_ANYONE)
//-----------------------------------------------------------------------------
using mozilla::dom::Promise;
namespace mozilla::net {
LazyLogModule gHttpLog(
"nsHttp");
LazyLogModule gHttpIOLog(
"HttpIO");
#ifdef ANDROID
static nsCString GetDeviceModelId() {
// Assumed to be running on the main thread
// We need the device property in either case
nsAutoCString deviceModelId;
nsCOMPtr<nsIPropertyBag2> infoService;
infoService = mozilla::components::SystemInfo::Service();
MOZ_ASSERT(infoService,
"Could not find a system info service");
nsAutoString androidDevice;
nsresult rv = infoService->GetPropertyAsAString(u
"device"_ns, androidDevice);
if (NS_SUCCEEDED(rv)) {
deviceModelId = NS_LossyConvertUTF16toASCII(androidDevice);
}
nsAutoCString deviceString;
rv = Preferences::GetCString(UA_PREF(
"device_string"), deviceString);
if (NS_SUCCEEDED(rv)) {
deviceString.Trim(
" ",
true,
true);
deviceString.ReplaceSubstring(
"%DEVICEID%"_ns, deviceModelId);
return std::move(deviceString);
}
return std::move(deviceModelId);
}
#endif
#ifdef XP_UNIX
static bool IsRunningUnderUbuntuSnap() {
# if defined(MOZ_WIDGET_GTK)
if (!widget::IsRunningUnderSnap()) {
return false;
}
char version[100];
if (PR_GetSystemInfo(PR_SI_RELEASE_BUILD, version,
sizeof(version)) ==
PR_SUCCESS) {
if (strstr(version,
"Ubuntu")) {
return true;
}
}
# endif
return false;
}
#endif
//-----------------------------------------------------------------------------
// nsHttpHandler <public>
//-----------------------------------------------------------------------------
StaticRefPtr<nsHttpHandler> gHttpHandler;
/* static */
already_AddRefed<nsHttpHandler> nsHttpHandler::GetInstance() {
if (!gHttpHandler) {
gHttpHandler =
new nsHttpHandler();
DebugOnly<nsresult> rv = gHttpHandler->Init();
MOZ_ASSERT(NS_SUCCEEDED(rv));
// There is code that may be executed during the final cycle collection
// shutdown and still referencing gHttpHandler.
ClearOnShutdown(&gHttpHandler, ShutdownPhase::CCPostLastCycleCollection);
}
RefPtr<nsHttpHandler> httpHandler = gHttpHandler;
return httpHandler.forget();
}
/// Derive the HTTP Accept header for image requests based on the enabled prefs
/// for non-universal image types. This may be overridden in its entirety by
/// the image.http.accept pref.
static nsCString ImageAcceptHeader() {
nsCString mimeTypes;
if (mozilla::StaticPrefs::image_avif_enabled()) {
mimeTypes.Append(
"image/avif,");
}
if (mozilla::StaticPrefs::image_jxl_enabled()) {
mimeTypes.Append(
"image/jxl,");
}
mimeTypes.Append(
"image/webp,");
// Default value as specified by fetch standard
// https://fetch.spec.whatwg.org/commit-snapshots/8dd73dbecfefdbef8f432164fb3a5b9785f7f520/#ref-for-header-list-contains%E2%91%A7
mimeTypes.Append(
"image/png,image/svg+xml,image/*;q=0.8,*/*;q=0.5");
return mimeTypes;
}
static nsCString DocumentAcceptHeader() {
// https://fetch.spec.whatwg.org/#document-accept-header-value
// The value specified by the fetch standard is
// `text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8`
nsCString mimeTypes(
"text/html,application/xhtml+xml,application/xml;q=0.9,");
// we also insert all of the image formats before */* when the pref is set
if (mozilla::StaticPrefs::network_http_accept_include_images()) {
if (mozilla::StaticPrefs::image_avif_enabled()) {
mimeTypes.Append(
"image/avif,");
}
if (mozilla::StaticPrefs::image_jxl_enabled()) {
mimeTypes.Append(
"image/jxl,");
}
mimeTypes.Append(
"image/webp,image/png,image/svg+xml,");
}
mimeTypes.Append(
"*/*;q=0.8");
return mimeTypes;
}
Atomic<
bool, Relaxed> nsHttpHandler::sParentalControlsEnabled(
false);
nsHttpHandler::nsHttpHandler()
: mIdleTimeout(PR_SecondsToInterval(10)),
mSpdyTimeout(
PR_SecondsToInterval(StaticPrefs::network_http_http2_timeout())),
mResponseTimeout(PR_SecondsToInterval(300)),
mImageAcceptHeader(ImageAcceptHeader()),
mDocumentAcceptHeader(DocumentAcceptHeader()),
mLastUniqueID(NowInSeconds()),
mIdempotencyKeySeed(mozilla::RandomUint64OrDie()),
mPrivateBrowsingIdempotencyKeySeed(mozilla::RandomUint64OrDie()),
mDebugObservations(
false),
mEnableAltSvc(
false),
mEnableAltSvcOE(
false),
mSpdyPingThreshold(PR_SecondsToInterval(
StaticPrefs::network_http_http2_ping_threshold())),
mSpdyPingTimeout(PR_SecondsToInterval(
StaticPrefs::network_http_http2_ping_timeout())) {
LOG((
"Creating nsHttpHandler [this=%p].\n",
this));
mUserAgentOverride.SetIsVoid(
true);
MOZ_ASSERT(!gHttpHandler,
"HTTP handler already created!");
nsCOMPtr<nsIXULRuntime> runtime;
runtime = mozilla::components::XULRuntime::Service();
if (runtime) {
runtime->GetProcessID(&mProcessId);
runtime->GetUniqueProcessID(&mUniqueProcessId);
}
}
nsHttpHandler::~nsHttpHandler() {
LOG((
"Deleting nsHttpHandler [this=%p]\n",
this));
// make sure the connection manager is shutdown
if (mConnMgr) {
nsresult rv = mConnMgr->Shutdown();
if (NS_FAILED(rv)) {
LOG(
(
"nsHttpHandler [this=%p] "
"failed to shutdown connection manager (%08x)\n",
this,
static_cast<uint32_t>(rv)));
}
mConnMgr = nullptr;
}
// Note: don't call NeckoChild::DestroyNeckoChild() here, as it's too late
// and it'll segfault. NeckoChild will get cleaned up by process exit.
nsHttp::DestroyAtomTable();
}
static const char* gCallbackPrefs[] = {
HTTP_PREF_PREFIX,
UA_PREF_PREFIX,
INTL_ACCEPT_LANGUAGES,
BROWSER_PREF(
"disk_cache_ssl"),
H2MANDATORY_SUITE,
HTTP_PREF(
"tcp_keepalive.short_lived_connections"),
HTTP_PREF(
"tcp_keepalive.long_lived_connections"),
SAFE_HINT_HEADER_VALUE,
SECURITY_PREFIX,
DOM_SECURITY_PREFIX,
"image.http.accept",
"image.avif.enabled",
"image.jxl.enabled",
nullptr,
};
nsresult nsHttpHandler::Init() {
nsresult rv;
LOG((
"nsHttpHandler::Init\n"));
MOZ_ASSERT(NS_IsMainThread());
// We should not create nsHttpHandler during shutdown, but we have some
// xpcshell tests doing this.
if (MOZ_UNLIKELY(AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdown) &&
!PR_GetEnv(
"XPCSHELL_TEST_PROFILE_DIR"))) {
MOZ_DIAGNOSTIC_CRASH(
"Try to init HttpHandler after shutdown");
return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
}
rv = nsHttp::CreateAtomTable();
if (NS_FAILED(rv))
return rv;
nsCOMPtr<nsIIOService> service;
service = mozilla::components::IO::Service(&rv);
if (NS_FAILED(rv)) {
NS_WARNING(
"unable to continue without io service");
return rv;
}
mIOService =
new nsMainThreadPtrHolder<nsIIOService>(
"nsHttpHandler::mIOService", service);
gIOService->LaunchSocketProcess();
if (IsNeckoChild()) NeckoChild::InitNeckoChild();
InitUserAgentComponents();
// This preference is only used in parent process.
if (!IsNeckoChild()) {
mActiveTabPriority =
Preferences::GetBool(HTTP_PREF(
"active_tab_priority"),
true);
if (XRE_IsParentProcess()) {
std::bitset<3> usageOfHTTPSRRPrefs;
usageOfHTTPSRRPrefs[0] = StaticPrefs::network_dns_upgrade_with_https_rr();
usageOfHTTPSRRPrefs[1] =
StaticPrefs::network_dns_use_https_rr_as_altsvc();
usageOfHTTPSRRPrefs[2] = StaticPrefs::network_dns_echconfig_enabled();
glean::networking::https_rr_prefs_usage.Set(
static_cast<uint32_t>(usageOfHTTPSRRPrefs.to_ulong()));
glean::networking::http3_enabled.Set(
StaticPrefs::network_http_http3_enable());
}
mActivityDistributor = components::HttpActivityDistributor::Service();
auto initQLogDir = [&]() {
if (!StaticPrefs::network_http_http3_enable_qlog()) {
return EmptyCString();
}
nsCOMPtr<nsIFile> qlogDir;
nsresult rv =
NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(qlogDir));
if (NS_WARN_IF(NS_FAILED(rv))) {
return EmptyCString();
}
nsAutoCString dirName(
"qlog_");
dirName.AppendInt(mProcessId);
rv = qlogDir->AppendNative(dirName);
if (NS_WARN_IF(NS_FAILED(rv))) {
return EmptyCString();
}
return qlogDir->HumanReadablePath();
};
mHttp3QlogDir = initQLogDir();
if (
const char* origin = PR_GetEnv(
"MOZ_FORCE_QUIC_ON")) {
nsCCharSeparatedTokenizer tokens(nsDependentCString(origin),
':');
nsAutoCString host;
int32_t port = 443;
if (tokens.hasMoreTokens()) {
host = tokens.nextToken();
if (tokens.hasMoreTokens()) {
nsresult res;
int32_t tmp = tokens.nextToken().ToInteger(&res);
if (NS_SUCCEEDED(res)) {
port = tmp;
}
}
mAltSvcMappingTemptativeMap.InsertOrUpdate(
host, MakeUnique<nsCString>(nsPrintfCString(
"h3=:%d", port)));
}
}
}
// monitor some preference changes
Preferences::RegisterPrefixCallbacks(nsHttpHandler::PrefsChanged,
gCallbackPrefs,
this);
PrefsChanged(nullptr);
mCompatFirefox.AssignLiteral(
"Firefox/" MOZILLA_UAVERSION);
nsCOMPtr<nsIXULAppInfo> appInfo;
appInfo = mozilla::components::XULRuntime::Service();
mAppName.AssignLiteral(MOZ_APP_UA_NAME);
if (mAppName.Length() == 0 && appInfo) {
// Try to get the UA name from appInfo, falling back to the name
appInfo->GetUAName(mAppName);
if (mAppName.Length() == 0) {
appInfo->GetName(mAppName);
}
appInfo->GetVersion(mAppVersion);
mAppName.StripChars(R
"( ()<>@,;:\"/[]?={})
");
}
else {
mAppVersion.AssignLiteral(MOZ_APP_UA_VERSION);
}
mMisc.AssignLiteral(
"rv:" MOZILLA_UAVERSION);
// Generate the spoofed User Agent for fingerprinting resistance.
nsRFPService::GetSpoofedUserAgent(mSpoofedUserAgent);
mSessionStartTime = NowInSeconds();
mHandlerActive =
true;
rv = InitConnectionMgr();
if (NS_FAILED(rv))
return rv;
mAltSvcCache = MakeUnique<AltSvcCache>();
mRequestContextService = RequestContextService::GetOrCreate();
#if defined(ANDROID) ||
defined(XP_IOS)
mProductSub.AssignLiteral(MOZILLA_UAVERSION);
#else
mProductSub.AssignLiteral(LEGACY_UA_GECKO_TRAIL);
#endif
#if DEBUG
// dump user agent prefs
LOG((
"> legacy-app-name = %s\n", mLegacyAppName.get()));
LOG((
"> legacy-app-version = %s\n", mLegacyAppVersion.get()));
LOG((
"> platform = %s\n", mPlatform.get()));
LOG((
"> oscpu = %s\n", mOscpu.get()));
LOG((
"> misc = %s\n", mMisc.get()));
LOG((
"> product = %s\n", mProduct.get()));
LOG((
"> product-sub = %s\n", mProductSub.get()));
LOG((
"> app-name = %s\n", mAppName.get()));
LOG((
"> app-version = %s\n", mAppVersion.get()));
LOG((
"> compat-firefox = %s\n", mCompatFirefox.get()));
LOG((
"> user-agent = %s\n", UserAgent(
false).get()));
#endif
// Startup the http category
// Bring alive the objects in the http-protocol-startup category
NS_CreateServicesFromCategory(
NS_HTTP_STARTUP_CATEGORY,
static_cast<nsISupports*>(
static_cast<
void*>(
this)),
NS_HTTP_STARTUP_TOPIC);
nsCOMPtr<nsIObserverService> obsService =
static_cast<nsIObserverService*>(gIOService);
if (obsService) {
// register the handler object as a weak callback as we don't need to worry
// about shutdown ordering.
obsService->AddObserver(
this,
"profile-change-net-teardown",
true);
obsService->AddObserver(
this,
"profile-change-net-restore",
true);
obsService->AddObserver(
this, NS_XPCOM_SHUTDOWN_OBSERVER_ID,
true);
obsService->AddObserver(
this,
"net:clear-active-logins",
true);
obsService->AddObserver(
this,
"net:prune-dead-connections",
true);
// Sent by the TorButton add-on in the Tor Browser
obsService->AddObserver(
this,
"net:prune-all-connections",
true);
obsService->AddObserver(
this,
"net:cancel-all-connections",
true);
obsService->AddObserver(
this,
"last-pb-context-exited",
true);
obsService->AddObserver(
this,
"browser:purge-session-history",
true);
obsService->AddObserver(
this, NS_NETWORK_LINK_TOPIC,
true);
obsService->AddObserver(
this,
"application-background",
true);
obsService->AddObserver(
this,
"psm:user-certificate-added",
true);
obsService->AddObserver(
this,
"psm:user-certificate-deleted",
true);
obsService->AddObserver(
this,
"intl:app-locales-changed",
true);
obsService->AddObserver(
this,
"browser-delayed-startup-finished",
true);
obsService->AddObserver(
this,
"network:reset-http3-excluded-list",
true);
obsService->AddObserver(
this,
"network:socket-process-crashed",
true);
obsService->AddObserver(
this,
"network:reset_third_party_roots_check",
true);
if (!IsNeckoChild()) {
obsService->AddObserver(
this,
"net:current-browser-id",
true);
}
// disabled as its a nop right now
// obsService->AddObserver(this, "net:failed-to-process-uri-content", true);
}
MakeNewRequestTokenBucket();
mWifiTickler =
new Tickler();
if (NS_FAILED(mWifiTickler->Init())) {
mWifiTickler = nullptr;
}
UpdateParentalControlsEnabled(
false /* wait for completion */);
return NS_OK;
}
void nsHttpHandler::UpdateParentalControlsEnabled(
bool waitForCompletion) {
// Child process does not have privileges to read parentals control state
if (!XRE_IsParentProcess()) {
return;
}
auto getParentalControlsTask = []() {
nsCOMPtr<nsIParentalControlsService> pc =
do_CreateInstance(
"@mozilla.org/parental-controls-service;1");
if (pc) {
bool localEnabled =
false;
pc->GetParentalControlsEnabled(&localEnabled);
sParentalControlsEnabled = localEnabled;
// Cache the state of parental controls via preference
if (!AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
Preferences::SetBool(
StaticPrefs::GetPrefName_network_parental_controls_cached_state(),
localEnabled);
}
}
};
if (waitForCompletion) {
getParentalControlsTask();
}
else {
// To avoid blocking on determining parental controls state, used the cached
// pref until the runnable completes
sParentalControlsEnabled =
mozilla::StaticPrefs::network_parental_controls_cached_state();
Unused << NS_DispatchToMainThreadQueue(
NS_NewRunnableFunction(
"GetParentalControlsEnabled",
std::move(getParentalControlsTask)),
mozilla::EventQueuePriority::Idle);
}
}
void nsHttpHandler::GenerateIdempotencyKeyForPost(
const uint32_t aPostId,
nsILoadInfo* aLoadInfo,
nsACString& aOutKey) {
MOZ_ASSERT(aLoadInfo);
OriginAttributes attrs = aLoadInfo->GetOriginAttributes();
// Create a SHA1 string using the origin attributes, session seed and the post
// id.
nsAutoCString sha1Input;
attrs.CreateSuffix(sha1Input);
sha1Input.AppendInt(aPostId);
sha1Input.AppendInt(attrs.IsPrivateBrowsing()
? mPrivateBrowsingIdempotencyKeySeed
: mIdempotencyKeySeed);
SHA1Sum sha1;
SHA1Sum::Hash hash;
sha1.update((sha1Input.get()), sha1Input.Length());
sha1.finish(hash);
uint64_t hashValue = BigEndian::readUint64(&hash);
aOutKey.Append(
"\"");
aOutKey.AppendInt(hashValue);
aOutKey.Append(
"\"");
}
const nsCString& nsHttpHandler::Http3QlogDir() {
if (StaticPrefs::network_http_http3_enable_qlog()) {
return mHttp3QlogDir;
}
return EmptyCString();
}
void nsHttpHandler::MakeNewRequestTokenBucket() {
LOG((
"nsHttpHandler::MakeNewRequestTokenBucket this=%p child=%d\n",
this,
IsNeckoChild()));
if (!mConnMgr || IsNeckoChild()) {
return;
}
RefPtr<EventTokenBucket> tokenBucket =
new EventTokenBucket(RequestTokenBucketHz(), RequestTokenBucketBurst());
// NOTE The thread or socket may be gone already.
nsresult rv = mConnMgr->UpdateRequestTokenBucket(tokenBucket);
if (NS_FAILED(rv)) {
LOG((
" failed to update request token bucket\n"));
}
}
nsresult nsHttpHandler::InitConnectionMgr() {
// Init ConnectionManager only on parent!
if (IsNeckoChild()) {
return NS_OK;
}
if (mConnMgr) {
return NS_OK;
}
if (nsIOService::UseSocketProcess(
true) && XRE_IsParentProcess()) {
mConnMgr =
new HttpConnectionMgrParent();
RefPtr<nsHttpHandler> self =
this;
auto task = [self]() {
RefPtr<HttpConnectionMgrParent> parent =
self->mConnMgr->AsHttpConnectionMgrParent();
RefPtr<SocketProcessParent> socketParent =
SocketProcessParent::GetSingleton();
Unused << socketParent->SendPHttpConnectionMgrConstructor(
parent,
HttpHandlerInitArgs(self->mLegacyAppName, self->mLegacyAppVersion,
self->mPlatform, self->mOscpu, self->mMisc,
self->mProduct, self->mProductSub, self->mAppName,
self->mAppVersion, self->mCompatFirefox,
self->mCompatDevice, self->mDeviceModelId));
};
gIOService->CallOrWaitForSocketProcess(std::move(task));
}
else {
MOZ_ASSERT(XRE_IsSocketProcess() || !nsIOService::UseSocketProcess());
mConnMgr =
new nsHttpConnectionMgr();
}
return mConnMgr->Init(mMaxUrgentExcessiveConns, mMaxConnections,
mMaxPersistentConnectionsPerServer,
mMaxPersistentConnectionsPerProxy, mMaxRequestDelay,
mThrottleEnabled, mThrottleSuspendFor,
mThrottleResumeFor, mThrottleHoldTime, mThrottleMaxTime,
mBeConservativeForProxy);
}
nsresult nsHttpHandler::AddStandardRequestHeaders(
nsHttpRequestHead* request,
bool isSecure,
ExtContentPolicyType aContentPolicyType,
bool aShouldResistFingerprinting) {
nsresult rv;
// Add the "User-Agent" header
rv = request->SetHeader(nsHttp::User_Agent,
UserAgent(aShouldResistFingerprinting),
false,
nsHttpHeaderArray::eVarietyRequestDefault);
if (NS_FAILED(rv))
return rv;
// MIME based content negotiation lives!
// Add the "Accept" header. Note, this is set as an override because the
// service worker expects to see it. The other "default" headers are
// hidden from service worker interception.
nsAutoCString accept;
if (aContentPolicyType == ExtContentPolicy::TYPE_DOCUMENT ||
aContentPolicyType == ExtContentPolicy::TYPE_SUBDOCUMENT) {
accept.Assign(mDocumentAcceptHeader);
}
else if (aContentPolicyType == ExtContentPolicy::TYPE_IMAGE ||
aContentPolicyType == ExtContentPolicy::TYPE_IMAGESET) {
accept.Assign(mImageAcceptHeader);
}
else if (aContentPolicyType == ExtContentPolicy::TYPE_STYLESHEET) {
accept.Assign(ACCEPT_HEADER_STYLE);
}
else {
accept.Assign(ACCEPT_HEADER_ALL);
}
rv = request->SetHeader(nsHttp::Accept, accept,
false,
nsHttpHeaderArray::eVarietyRequestOverride);
if (NS_FAILED(rv))
return rv;
// Add the "Accept-Language" header. This header is also exposed to the
// service worker.
if (mAcceptLanguagesIsDirty) {
rv = SetAcceptLanguages();
MOZ_ASSERT(NS_SUCCEEDED(rv));
}
// Add the "Accept-Language" header
if (!mAcceptLanguages.IsEmpty()) {
rv = request->SetHeader(nsHttp::Accept_Language, mAcceptLanguages,
false,
nsHttpHeaderArray::eVarietyRequestOverride);
if (NS_FAILED(rv))
return rv;
}
// Add the "Accept-Encoding" header
if (isSecure) {
rv = request->SetHeader(nsHttp::Accept_Encoding, mHttpsAcceptEncodings,
false, nsHttpHeaderArray::eVarietyRequestDefault);
}
else {
rv = request->SetHeader(nsHttp::Accept_Encoding, mHttpAcceptEncodings,
false, nsHttpHeaderArray::eVarietyRequestDefault);
}
if (NS_FAILED(rv))
return rv;
// add the "Send Hint" header
if (mSafeHintEnabled || sParentalControlsEnabled) {
rv = request->SetHeader(nsHttp::Prefer,
"safe"_ns,
false,
nsHttpHeaderArray::eVarietyRequestDefault);
if (NS_FAILED(rv))
return rv;
}
return NS_OK;
}
nsresult nsHttpHandler::AddConnectionHeader(nsHttpRequestHead* request,
uint32_t caps) {
// RFC2616 section 19.6.2 states that the "Connection: keep-alive"
// and "Keep-alive" request headers should not be sent by HTTP/1.1
// user-agents. But this is not a problem in practice, and the
// alternative proxy-connection is worse. see 570283
constexpr
auto close =
"close"_ns;
constexpr
auto keepAlive =
"keep-alive"_ns;
const nsLiteralCString* connectionType = &close;
if (caps & NS_HTTP_ALLOW_KEEPALIVE) {
connectionType = &keepAlive;
}
return request->SetHeader(nsHttp::Connection, *connectionType);
}
bool nsHttpHandler::IsAcceptableEncoding(
const char* enc,
bool isSecure) {
if (!enc)
return false;
// we used to accept x-foo anytime foo was acceptable, but that's just
// continuing bad behavior.. so limit it to known x-* patterns
bool rv;
if (isSecure) {
rv = nsHttp::FindToken(mHttpsAcceptEncodings.get(), enc, HTTP_LWS
",") !=
nullptr;
}
else {
rv = nsHttp::FindToken(mHttpAcceptEncodings.get(), enc, HTTP_LWS
",") !=
nullptr;
}
// gzip and deflate are inherently acceptable in modern HTTP - always
// process them if a stream converter can also be found.
if (!rv &&
(!nsCRT::strcasecmp(enc,
"gzip") || !nsCRT::strcasecmp(enc,
"deflate") ||
!nsCRT::strcasecmp(enc,
"x-gzip") ||
!nsCRT::strcasecmp(enc,
"x-deflate"))) {
rv =
true;
}
LOG((
"nsHttpHandler::IsAceptableEncoding %s https=%d %d\n", enc, isSecure,
rv));
return rv;
}
nsISiteSecurityService* nsHttpHandler::GetSSService() {
if (!mSSService) {
nsCOMPtr<nsISiteSecurityService> service;
service = mozilla::components::SiteSecurity::Service();
mSSService =
new nsMainThreadPtrHolder<nsISiteSecurityService>(
"nsHttpHandler::mSSService", service);
}
return mSSService;
}
nsICookieService* nsHttpHandler::GetCookieService() {
if (!mCookieService) {
nsCOMPtr<nsICookieService> service =
do_GetService(NS_COOKIESERVICE_CONTRACTID);
mCookieService =
new nsMainThreadPtrHolder<nsICookieService>(
"nsHttpHandler::mCookieService", service);
}
return mCookieService;
}
nsresult nsHttpHandler::GetIOService(nsIIOService** result) {
NS_ENSURE_ARG_POINTER(result);
*result = do_AddRef(mIOService.get()).take();
return NS_OK;
}
void nsHttpHandler::NotifyObservers(nsIChannel* chan,
const char* event) {
LOG((
"nsHttpHandler::NotifyObservers [this=%p chan=%p event=\"%s\
"]\n",
this,
chan, event));
nsCOMPtr<nsIObserverService> obsService = services::GetObserverService();
if (obsService) obsService->NotifyObservers(chan, event, nullptr);
}
nsresult nsHttpHandler::AsyncOnChannelRedirect(
nsIChannel* oldChan, nsIChannel* newChan, uint32_t flags,
nsIEventTarget* mainThreadEventTarget) {
MOZ_ASSERT(NS_IsMainThread() && (oldChan && newChan));
nsCOMPtr<nsIURI> oldURI;
oldChan->GetURI(getter_AddRefs(oldURI));
MOZ_ASSERT(oldURI);
nsCOMPtr<nsIURI> newURI;
newChan->GetURI(getter_AddRefs(newURI));
MOZ_ASSERT(newURI);
PrepareForAntiTrackingRedirectHeuristic(oldChan, oldURI, newChan, newURI);
DynamicFpiRedirectHeuristic(oldChan, oldURI, newChan, newURI);
// TODO E10S This helper has to be initialized on the other process
RefPtr<nsAsyncRedirectVerifyHelper> redirectCallbackHelper =
new nsAsyncRedirectVerifyHelper();
return redirectCallbackHelper->Init(oldChan, newChan, flags,
mainThreadEventTarget);
}
/* static */
nsresult nsHttpHandler::GenerateHostPort(
const nsCString& host, int32_t port,
nsACString& hostLine) {
return NS_GenerateHostPort(host, port, hostLine);
}
// static
uint8_t nsHttpHandler::UrgencyFromCoSFlags(uint32_t cos,
int32_t aSupportsPriority) {
uint8_t urgency;
if (cos & nsIClassOfService::UrgentStart) {
// coming from an user interaction => response should be the highest
// priority
urgency = 1;
}
else if (cos & nsIClassOfService::Leader) {
// main html document normal priority
urgency = 2;
}
else if (cos & nsIClassOfService::Unblocked) {
urgency = 3;
}
else if (cos & nsIClassOfService::Follower) {
urgency = 4;
}
else if (cos & nsIClassOfService::Speculative) {
urgency = 6;
}
else if (cos & nsIClassOfService::Background) {
// background tasks can be deprioritzed to the lowest priority
urgency = 6;
}
else if (cos & nsIClassOfService::Tail) {
urgency = mozilla::StaticPrefs::network_http_tailing_urgency();
}
else {
// all others get a lower priority than the main html document
urgency = 4;
}
int8_t adjustment = 0;
if (mozilla::StaticPrefs::network_fetchpriority_adjust_urgency()) {
if (aSupportsPriority <= nsISupportsPriority::PRIORITY_HIGHEST) {
adjustment = -2;
}
else if (aSupportsPriority <= nsISupportsPriority::PRIORITY_HIGH) {
adjustment = -1;
}
else if (aSupportsPriority >= nsISupportsPriority::PRIORITY_LOWEST) {
adjustment = 2;
}
else if (aSupportsPriority >= nsISupportsPriority::PRIORITY_LOW) {
adjustment = 1;
}
}
auto adjustUrgency = [](uint8_t u, int8_t a) -> uint8_t {
int16_t result =
static_cast<int16_t>(u) + a;
if (result <= 0) {
return 0;
}
if (result >= 6) {
return 6;
}
return result;
};
return adjustUrgency(urgency, adjustment);
}
//-----------------------------------------------------------------------------
// nsHttpHandler <private>
//-----------------------------------------------------------------------------
const nsCString& nsHttpHandler::UserAgent(
bool aShouldResistFingerprinting) {
if (aShouldResistFingerprinting && !mSpoofedUserAgent.IsEmpty()) {
LOG((
"using spoofed userAgent : %s\n", mSpoofedUserAgent.get()));
return mSpoofedUserAgent;
}
if (!mUserAgentOverride.IsVoid()) {
LOG((
"using general.useragent.override : %s\n", mUserAgentOverride.get()));
return mUserAgentOverride;
}
if (mUserAgentIsDirty) {
BuildUserAgent();
mUserAgentIsDirty =
false;
}
return mUserAgent;
}
void nsHttpHandler::BuildUserAgent() {
LOG((
"nsHttpHandler::BuildUserAgent\n"));
MOZ_ASSERT(!mLegacyAppName.IsEmpty() && !mLegacyAppVersion.IsEmpty(),
"HTTP cannot send practical requests without this much");
// preallocate to worst-case size, which should always be better
// than if we didn't preallocate at all.
mUserAgent.SetCapacity(mLegacyAppName.Length() + mLegacyAppVersion.Length() +
#ifndef UA_SPARE_PLATFORM
mPlatform.Length() +
#endif
mOscpu.Length() + mMisc.Length() + mProduct.Length() +
mProductSub.Length() + mAppName.Length() +
mAppVersion.Length() + mCompatFirefox.Length() +
mCompatDevice.Length() + mDeviceModelId.Length() + 13);
// Application portion
mUserAgent.Assign(mLegacyAppName);
mUserAgent +=
'/';
mUserAgent += mLegacyAppVersion;
mUserAgent +=
' ';
// Application comment
mUserAgent +=
'(';
#ifndef UA_SPARE_PLATFORM
if (!mPlatform.IsEmpty()) {
mUserAgent += mPlatform;
mUserAgent.AppendLiteral(
"; ");
}
#endif
if (!mCompatDevice.IsEmpty()) {
mUserAgent += mCompatDevice;
mUserAgent.AppendLiteral(
"; ");
}
else if (!mOscpu.IsEmpty()) {
mUserAgent += mOscpu;
mUserAgent.AppendLiteral(
"; ");
}
if (!mDeviceModelId.IsEmpty()) {
mUserAgent += mDeviceModelId;
mUserAgent.AppendLiteral(
"; ");
}
mUserAgent += mMisc;
mUserAgent +=
')';
// Product portion
mUserAgent +=
' ';
mUserAgent += mProduct;
mUserAgent +=
'/';
mUserAgent += mProductSub;
bool isFirefox = mAppName.EqualsLiteral(
"Firefox");
if (isFirefox || mCompatFirefoxEnabled) {
// "Firefox/x.y" (compatibility) app token
mUserAgent +=
' ';
mUserAgent += mCompatFirefox;
}
if (!isFirefox) {
// App portion
mUserAgent +=
' ';
mUserAgent += mAppName;
mUserAgent +=
'/';
mUserAgent += mAppVersion;
}
}
#ifdef XP_WIN
// Hardcode the reported Windows version to 10.0. This way, Microsoft doesn't
// get to change Web compat-sensitive values without our veto. The compat-
// sensitivity keeps going up as 10.0 stays as the current value for longer
// and longer. If the system-reported version ever changes, we'll be able to
// take our time to evaluate the Web compat impact instead of having to
// scramble to react like happened with macOS changing from 10.x to 11.x.
# define OSCPU_WINDOWS
"Windows NT 10.0"
# define OSCPU_WIN64 OSCPU_WINDOWS
"; Win64; x64"
#endif
void nsHttpHandler::InitUserAgentComponents() {
// Don't build user agent components in socket process, since the system info
// is not available.
if (XRE_IsSocketProcess()) {
mUserAgentIsDirty =
true;
return;
}
// Gather platform.
mPlatform.AssignLiteral(
#if defined(ANDROID)
"Android"
#elif defined(XP_WIN)
"Windows"
#elif defined(XP_MACOSX)
"Macintosh"
#elif defined(XP_IOS)
"iPhone"
#elif defined(XP_UNIX)
// We historically have always had X11 here,
// and there seems little a webpage can sensibly do
// based on it being something else, so use X11 for
// backwards compatibility in all cases.
"X11"
#endif
);
#ifdef XP_UNIX
if (IsRunningUnderUbuntuSnap()) {
mPlatform.AppendLiteral(
"; Ubuntu");
}
#endif
#ifdef ANDROID
nsCOMPtr<nsIPropertyBag2> infoService;
infoService = mozilla::components::SystemInfo::Service();
MOZ_ASSERT(infoService,
"Could not find a system info service");
nsresult rv;
// Add the Android version number to the Fennec platform identifier.
nsAutoString androidVersion;
rv = infoService->GetPropertyAsAString(u
"release_version"_ns, androidVersion);
MOZ_ASSERT_IF(
NS_SUCCEEDED(rv),
// Like version "9"
(androidVersion.Length() == 1 && std::isdigit(androidVersion[0])) ||
// Or like version "8.1", "10", or "12.1"
(androidVersion.Length() >= 2 && std::isdigit(androidVersion[0]) &&
(androidVersion[1] == u
'.' || std::isdigit(androidVersion[1]))));
// Spoof version "Android 10" for Android OS versions < 10 to reduce their
// fingerprintable user information. For Android OS versions >= 10, report
// the real OS version because some enterprise websites only want to permit
// clients with recent OS version (like bug 1876742). Two leading digits
// in the version string means the version number is >= 10.
mPlatform +=
" ";
if (NS_SUCCEEDED(rv) && androidVersion.Length() >= 2 &&
std::isdigit(androidVersion[0]) && std::isdigit(androidVersion[1])) {
mPlatform += NS_LossyConvertUTF16toASCII(androidVersion);
}
else {
mPlatform.AppendLiteral(
"10");
}
// Add the `Mobile` or `TV` token when running on device.
bool isTV;
rv = infoService->GetPropertyAsBool(u
"tv"_ns, &isTV);
if (NS_SUCCEEDED(rv) && isTV) {
mCompatDevice.AssignLiteral(
"TV");
}
else {
mCompatDevice.AssignLiteral(
"Mobile");
}
if (Preferences::GetBool(UA_PREF(
"use_device"),
false)) {
mDeviceModelId = mozilla::net::GetDeviceModelId();
}
#endif // ANDROID
#if defined(XP_IOS)
// Freeze the iOS version to 18.0, use an underscore separator to avoid
// detection as macOS.
mCompatDevice.AssignLiteral(
"CPU iPhone OS 18_0 like Mac OS X");
#endif
// Gather OS/CPU.
#if defined(XP_WIN)
# if defined _M_X64 ||
defined _M_AMD64
mOscpu.AssignLiteral(OSCPU_WIN64);
# elif
defined(_ARM64_)
// Report ARM64 Windows 11+ as x86_64 and Windows 10 as x86. Windows 11+
// supports x86_64 emulation, but Windows 10 only supports x86 emulation.
if (IsWin11OrLater()) {
mOscpu.AssignLiteral(OSCPU_WIN64);
}
else {
mOscpu.AssignLiteral(OSCPU_WINDOWS);
}
# else
BOOL isWow64 =
FALSE;
if (!IsWow64Process(GetCurrentProcess(), &isWow64)) {
isWow64 =
FALSE;
}
if (isWow64) {
mOscpu.AssignLiteral(OSCPU_WIN64);
}
else {
mOscpu.AssignLiteral(OSCPU_WINDOWS);
}
# endif
#elif defined(XP_MACOSX)
mOscpu.AssignLiteral(
"Intel Mac OS X 10.15");
#elif defined(ANDROID)
mOscpu.AssignLiteral(
"Linux armv81");
#elif defined(XP_IOS)
mOscpu.AssignLiteral(
"iPhone");
#else
mOscpu.AssignLiteral(
"Linux x86_64");
#endif
mUserAgentIsDirty =
true;
}
#ifdef XP_MACOSX
void nsHttpHandler::InitMSAuthorities() {
if (!StaticPrefs::network_http_microsoft_entra_sso_enabled()) {
return;
}
nsAutoCString authorityList;
if (NS_FAILED(Preferences::GetCString(
"network.microsoft-sso-authority-list",
authorityList))) {
return;
}
mMSAuthorities.Clear();
// Normalize the MS authority list
nsCCharSeparatedTokenizer tokenizer(authorityList,
',');
while (tokenizer.hasMoreTokens()) {
const nsDependentCSubstring& token = tokenizer.nextToken();
mMSAuthorities.Insert(token);
}
}
#endif
uint32_t nsHttpHandler::MaxSocketCount() {
PR_CallOnce(&nsSocketTransportService::gMaxCountInitOnce,
nsSocketTransportService::DiscoverMaxCount);
// Don't use the full max count because sockets can be held in
// the persistent connection pool for a long time and that could
// starve other users.
uint32_t maxCount = nsSocketTransportService::gMaxCount;
if (maxCount <= 8) {
maxCount = 1;
}
else {
maxCount -= 8;
}
return maxCount;
}
// static
void nsHttpHandler::PrefsChanged(
const char* pref,
void* self) {
static_cast<nsHttpHandler*>(self)->PrefsChanged(pref);
}
void nsHttpHandler::PrefsChanged(
const char* pref) {
nsresult rv = NS_OK;
int32_t val;
LOG((
"nsHttpHandler::PrefsChanged [pref=%s]\n", pref));
if (pref) {
gIOService->NotifySocketProcessPrefsChanged(pref);
}
#define PREF_CHANGED(p) ((pref == nullptr) || !strcmp(pref, p))
#define MULTI_PREF_CHANGED(p) \
((pref == nullptr) || !strncmp(pref, p,
sizeof(p) - 1))
// If a security pref changed, lets clear our connection pool reuse
if (MULTI_PREF_CHANGED(SECURITY_PREFIX)) {
LOG((
"nsHttpHandler::PrefsChanged Security Pref Changed %s\n", pref));
if (mConnMgr) {
rv = mConnMgr->DoShiftReloadConnectionCleanup();
if (NS_FAILED(rv)) {
LOG(
(
"nsHttpHandler::PrefsChanged "
"DoShiftReloadConnectionCleanup failed (%08x)\n",
static_cast<uint32_t>(rv)));
}
rv = mConnMgr->PruneDeadConnections();
if (NS_FAILED(rv)) {
LOG(
(
"nsHttpHandler::PrefsChanged "
"PruneDeadConnections failed (%08x)\n",
static_cast<uint32_t>(rv)));
}
}
}
//
// UA components
//
bool cVar =
false;
if (PREF_CHANGED(UA_PREF(
"compatMode.firefox"))) {
rv = Preferences::GetBool(UA_PREF(
"compatMode.firefox"), &cVar);
mCompatFirefoxEnabled = (NS_SUCCEEDED(rv) && cVar);
mUserAgentIsDirty =
true;
}
// general.useragent.override
if (PREF_CHANGED(UA_PREF(
"override"))) {
Preferences::GetCString(UA_PREF(
"override"), mUserAgentOverride);
mUserAgentIsDirty =
true;
}
#ifdef ANDROID
// general.useragent.use_device
if (PREF_CHANGED(UA_PREF(
"use_device"))) {
if (Preferences::GetBool(UA_PREF(
"use_device"),
false)) {
if (!XRE_IsSocketProcess()) {
mDeviceModelId = mozilla::net::GetDeviceModelId();
if (gIOService->SocketProcessReady()) {
RefPtr<SocketProcessParent> socketParent =
SocketProcessParent::GetSingleton();
Unused << socketParent->SendUpdateDeviceModelId(mDeviceModelId);
}
}
}
else {
mDeviceModelId.Truncate();
}
mUserAgentIsDirty =
true;
}
#endif
//
// HTTP options
//
if (PREF_CHANGED(HTTP_PREF(
"keep-alive.timeout"))) {
rv = Preferences::GetInt(HTTP_PREF(
"keep-alive.timeout"), &val);
if (NS_SUCCEEDED(rv)) {
mIdleTimeout = PR_SecondsToInterval(std::clamp(val, 1, 0xffff));
}
}
if (PREF_CHANGED(HTTP_PREF(
"request.max-attempts"))) {
rv = Preferences::GetInt(HTTP_PREF(
"request.max-attempts"), &val);
if (NS_SUCCEEDED(rv)) {
mMaxRequestAttempts = (uint16_t)std::clamp(val, 1, 0xffff);
}
}
if (PREF_CHANGED(HTTP_PREF(
"request.max-start-delay"))) {
rv = Preferences::GetInt(HTTP_PREF(
"request.max-start-delay"), &val);
if (NS_SUCCEEDED(rv)) {
mMaxRequestDelay = (uint16_t)std::clamp(val, 0, 0xffff);
if (mConnMgr) {
rv = mConnMgr->UpdateParam(nsHttpConnectionMgr::MAX_REQUEST_DELAY,
mMaxRequestDelay);
if (NS_FAILED(rv)) {
LOG(
(
"nsHttpHandler::PrefsChanged (request.max-start-delay)"
"UpdateParam failed (%08x)\n",
static_cast<uint32_t>(rv)));
}
}
}
}
if (PREF_CHANGED(HTTP_PREF(
"response.timeout"))) {
rv = Preferences::GetInt(HTTP_PREF(
"response.timeout"), &val);
if (NS_SUCCEEDED(rv)) {
mResponseTimeout = PR_SecondsToInterval(std::clamp(val, 0, 0xffff));
}
}
if (PREF_CHANGED(HTTP_PREF(
"network-changed.timeout"))) {
rv = Preferences::GetInt(HTTP_PREF(
"network-changed.timeout"), &val);
if (NS_SUCCEEDED(rv)) {
mNetworkChangedTimeout = std::clamp(val, 1, 600) * 1000;
}
}
if (PREF_CHANGED(HTTP_PREF(
"max-connections"))) {
rv = Preferences::GetInt(HTTP_PREF(
"max-connections"), &val);
if (NS_SUCCEEDED(rv)) {
mMaxConnections =
(uint16_t)std::clamp((uint32_t)val, (uint32_t)1, MaxSocketCount());
if (mConnMgr) {
rv = mConnMgr->UpdateParam(nsHttpConnectionMgr::MAX_CONNECTIONS,
mMaxConnections);
if (NS_FAILED(rv)) {
LOG(
(
"nsHttpHandler::PrefsChanged (max-connections)"
"UpdateParam failed (%08x)\n",
static_cast<uint32_t>(rv)));
}
}
}
}
if (PREF_CHANGED(
HTTP_PREF(
"max-urgent-start-excessive-connections-per-host"))) {
rv = Preferences::GetInt(
HTTP_PREF(
"max-urgent-start-excessive-connections-per-host"), &val);
if (NS_SUCCEEDED(rv)) {
mMaxUrgentExcessiveConns = (uint8_t)std::clamp(val, 1, 0xff);
if (mConnMgr) {
rv = mConnMgr->UpdateParam(nsHttpConnectionMgr::MAX_URGENT_START_Q,
mMaxUrgentExcessiveConns);
if (NS_FAILED(rv)) {
LOG(
(
"nsHttpHandler::PrefsChanged "
"(max-urgent-start-excessive-connections-per-host)"
"UpdateParam failed (%08x)\n",
static_cast<uint32_t>(rv)));
}
}
}
}
if (PREF_CHANGED(HTTP_PREF(
"max-persistent-connections-per-server"))) {
rv = Preferences::GetInt(HTTP_PREF(
"max-persistent-connections-per-server"),
&val);
if (NS_SUCCEEDED(rv)) {
mMaxPersistentConnectionsPerServer = (uint8_t)std::clamp(val, 1, 0xff);
if (mConnMgr) {
rv = mConnMgr->UpdateParam(
nsHttpConnectionMgr::MAX_PERSISTENT_CONNECTIONS_PER_HOST,
mMaxPersistentConnectionsPerServer);
if (NS_FAILED(rv)) {
LOG(
(
"nsHttpHandler::PrefsChanged "
"(max-persistent-connections-per-server)"
"UpdateParam failed (%08x)\n",
static_cast<uint32_t>(rv)));
}
}
}
}
if (PREF_CHANGED(HTTP_PREF(
"max-persistent-connections-per-proxy"))) {
rv = Preferences::GetInt(HTTP_PREF(
"max-persistent-connections-per-proxy"),
&val);
if (NS_SUCCEEDED(rv)) {
mMaxPersistentConnectionsPerProxy = (uint8_t)std::clamp(val, 1, 0xff);
if (mConnMgr) {
rv = mConnMgr->UpdateParam(
nsHttpConnectionMgr::MAX_PERSISTENT_CONNECTIONS_PER_PROXY,
mMaxPersistentConnectionsPerProxy);
if (NS_FAILED(rv)) {
LOG(
(
"nsHttpHandler::PrefsChanged "
"(max-persistent-connections-per-proxy)"
"UpdateParam failed (%08x)\n",
static_cast<uint32_t>(rv)));
}
}
}
}
if (PREF_CHANGED(HTTP_PREF(
"redirection-limit"))) {
rv = Preferences::GetInt(HTTP_PREF(
"redirection-limit"), &val);
if (NS_SUCCEEDED(rv)) mRedirectionLimit = (uint8_t)std::clamp(val, 0, 0xff);
}
if (PREF_CHANGED(HTTP_PREF(
"connection-retry-timeout"))) {
rv = Preferences::GetInt(HTTP_PREF(
"connection-retry-timeout"), &val);
if (NS_SUCCEEDED(rv)) mIdleSynTimeout = (uint16_t)std::clamp(val, 0, 3000);
}
if (PREF_CHANGED(HTTP_PREF(
"fast-fallback-to-IPv4"))) {
rv = Preferences::GetBool(HTTP_PREF(
"fast-fallback-to-IPv4"), &cVar);
if (NS_SUCCEEDED(rv)) mFastFallbackToIPv4 = cVar;
}
if (PREF_CHANGED(HTTP_PREF(
"fallback-connection-timeout"))) {
rv = Preferences::GetInt(HTTP_PREF(
"fallback-connection-timeout"), &val);
if (NS_SUCCEEDED(rv)) {
mFallbackSynTimeout = (uint16_t)std::clamp(val, 0, 10 * 60);
}
}
if (PREF_CHANGED(HTTP_PREF(
"version"))) {
nsAutoCString httpVersion;
Preferences::GetCString(HTTP_PREF(
"version"), httpVersion);
if (!httpVersion.IsVoid()) {
if (httpVersion.EqualsLiteral(
"1.1")) {
mHttpVersion = HttpVersion::v1_1;
}
else if (httpVersion.EqualsLiteral(
"0.9")) {
mHttpVersion = HttpVersion::v0_9;
}
else {
mHttpVersion = HttpVersion::v1_0;
}
}
}
if (PREF_CHANGED(HTTP_PREF(
"proxy.version"))) {
nsAutoCString httpVersion;
Preferences::GetCString(HTTP_PREF(
"proxy.version"), httpVersion);
if (!httpVersion.IsVoid()) {
if (httpVersion.EqualsLiteral(
"1.1")) {
mProxyHttpVersion = HttpVersion::v1_1;
}
else {
mProxyHttpVersion = HttpVersion::v1_0;
}
// it does not make sense to issue a HTTP/0.9 request to a proxy server
}
}
if (PREF_CHANGED(HTTP_PREF(
"proxy.respect-be-conservative"))) {
rv =
Preferences::GetBool(HTTP_PREF(
"proxy.respect-be-conservative"), &cVar);
if (NS_SUCCEEDED(rv)) {
mBeConservativeForProxy = cVar;
if (mConnMgr) {
Unused << mConnMgr->UpdateParam(
nsHttpConnectionMgr::PROXY_BE_CONSERVATIVE,
static_cast<int32_t>(mBeConservativeForProxy));
}
}
}
if (PREF_CHANGED(HTTP_PREF(
"qos"))) {
rv = Preferences::GetInt(HTTP_PREF(
"qos"), &val);
if (NS_SUCCEEDED(rv)) mQoSBits = (uint8_t)std::clamp(val, 0, 0xff);
}
if (PREF_CHANGED(HTTP_PREF(
"accept-encoding"))) {
nsAutoCString acceptEncodings;
rv = Preferences::GetCString(HTTP_PREF(
"accept-encoding"), acceptEncodings);
if (NS_SUCCEEDED(rv)) {
rv = SetAcceptEncodings(acceptEncodings.get(),
false);
MOZ_ASSERT(NS_SUCCEEDED(rv));
}
}
if (PREF_CHANGED(HTTP_PREF(
"accept-encoding.secure"))) {
nsAutoCString acceptEncodings;
rv = Preferences::GetCString(HTTP_PREF(
"accept-encoding.secure"),
acceptEncodings);
if (NS_SUCCEEDED(rv)) {
rv = SetAcceptEncodings(acceptEncodings.get(),
true);
MOZ_ASSERT(NS_SUCCEEDED(rv));
}
}
if (PREF_CHANGED(HTTP_PREF(
"default-socket-type"))) {
nsAutoCString sval;
rv = Preferences::GetCString(HTTP_PREF(
"default-socket-type"), sval);
if (NS_SUCCEEDED(rv)) {
if (sval.IsEmpty()) {
mDefaultSocketType.SetIsVoid(
true);
}
else {
// verify that this socket type is actually valid
nsCOMPtr<nsISocketProviderService> sps =
nsSocketProviderService::GetOrCreate();
if (sps) {
nsCOMPtr<nsISocketProvider> sp;
rv = sps->GetSocketProvider(sval.get(), getter_AddRefs(sp));
if (NS_SUCCEEDED(rv)) {
// OK, this looks like a valid socket provider.
mDefaultSocketType.Assign(sval);
}
}
}
}
}
if (PREF_CHANGED(HTTP_PREF(
"prompt-temp-redirect"))) {
rv = Preferences::GetBool(HTTP_PREF(
"prompt-temp-redirect"), &cVar);
if (NS_SUCCEEDED(rv)) {
mPromptTempRedirect = cVar;
}
}
if (PREF_CHANGED(HTTP_PREF(
"assoc-req.enforce"))) {
cVar =
false;
rv = Preferences::GetBool(HTTP_PREF(
"assoc-req.enforce"), &cVar);
if (NS_SUCCEEDED(rv)) mEnforceAssocReq = cVar;
}
// enable Persistent caching for HTTPS - bug#205921
if (PREF_CHANGED(BROWSER_PREF(
"disk_cache_ssl"))) {
cVar =
false;
rv = Preferences::GetBool(BROWSER_PREF(
"disk_cache_ssl"), &cVar);
if (NS_SUCCEEDED(rv)) mEnablePersistentHttpsCaching = cVar;
}
if (PREF_CHANGED(HTTP_PREF(
"phishy-userpass-length"))) {
rv = Preferences::GetInt(HTTP_PREF(
"phishy-userpass-length"), &val);
if (NS_SUCCEEDED(rv)) {
mPhishyUserPassLength = (uint8_t)std::clamp(val, 0, 0xff);
}
}
if (PREF_CHANGED(HTTP_PREF(
"http2.timeout"))) {
mSpdyTimeout = PR_SecondsToInterval(
std::clamp(StaticPrefs::network_http_http2_timeout(), 1, 0xffff));
}
if (PREF_CHANGED(HTTP_PREF(
"http2.chunk-size"))) {
// keep this within http/2 ranges of 1 to 2^24-1
mSpdySendingChunkSize = (uint32_t)std::clamp(
StaticPrefs::network_http_http2_chunk_size(), 1, 0xffffff);
}
// The amount of idle seconds on a http2 connection before initiating a
// server ping. 0 will disable.
if (PREF_CHANGED(HTTP_PREF(
"http2.ping-threshold"))) {
mSpdyPingThreshold = PR_SecondsToInterval((uint16_t)std::clamp(
StaticPrefs::network_http_http2_ping_threshold(), 0, 0x7fffffff));
}
// The amount of seconds to wait for a http2 ping response before
// closing the session.
if (PREF_CHANGED(HTTP_PREF(
"http2.ping-timeout"))) {
mSpdyPingTimeout = PR_SecondsToInterval((uint16_t)std::clamp(
StaticPrefs::network_http_http2_ping_timeout(), 0, 0x7fffffff));
}
if (PREF_CHANGED(HTTP_PREF(
"altsvc.enabled"))) {
rv = Preferences::GetBool(HTTP_PREF(
"altsvc.enabled"), &cVar);
if (NS_SUCCEEDED(rv)) mEnableAltSvc = cVar;
}
if (PREF_CHANGED(HTTP_PREF(
"altsvc.oe"))) {
rv = Preferences::GetBool(HTTP_PREF(
"altsvc.oe"), &cVar);
if (NS_SUCCEEDED(rv)) mEnableAltSvcOE = cVar;
}
if (PREF_CHANGED(HTTP_PREF(
"http2.push-allowance"))) {
mSpdyPushAllowance =
static_cast<uint32_t>(
std::clamp(StaticPrefs::network_http_http2_push_allowance(), 1024,
static_cast<int32_t>(ASpdySession::kInitialRwin)));
}
if (PREF_CHANGED(HTTP_PREF(
"http2.pull-allowance"))) {
mSpdyPullAllowance =
static_cast<uint32_t>(std::clamp(
StaticPrefs::network_http_http2_pull_allowance(), 1024, 0x7fffffff));
}
if (PREF_CHANGED(HTTP_PREF(
"http2.default-concurrent"))) {
mDefaultSpdyConcurrent =
static_cast<uint32_t>(std::max<int32_t>(
std::min<int32_t>(StaticPrefs::network_http_http2_default_concurrent(),
9999),
1));
}
// If http2.send-buffer-size is non-zero, the size to set the TCP
// sendbuffer to once the stream has surpassed this number of bytes uploaded
if (PREF_CHANGED(HTTP_PREF(
"http2.send-buffer-size"))) {
mSpdySendBufferSize = (uint32_t)std::clamp(
StaticPrefs::network_http_http2_send_buffer_size(), 1500, 0x7fffffff);
}
// The maximum amount of time to wait for socket transport to be
// established
if (PREF_CHANGED(HTTP_PREF(
"connection-timeout"))) {
rv = Preferences::GetInt(HTTP_PREF(
"connection-timeout"), &val);
if (NS_SUCCEEDED(rv)) {
// the pref is in seconds, but the variable is in milliseconds
mConnectTimeout = std::clamp(val, 1, 0xffff) * PR_MSEC_PER_SEC;
}
}
// The maximum amount of time to wait for a tls handshake to finish.
if (PREF_CHANGED(HTTP_PREF(
"tls-handshake-timeout"))) {
rv = Preferences::GetInt(HTTP_PREF(
"tls-handshake-timeout"), &val);
if (NS_SUCCEEDED(rv)) {
// the pref is in seconds, but the variable is in milliseconds
mTLSHandshakeTimeout = std::clamp(val, 1, 0xffff) * PR_MSEC_PER_SEC;
}
}
// The maximum number of current global half open sockets allowable
// for starting a new speculative connection.
if (PREF_CHANGED(HTTP_PREF(
"speculative-parallel-limit"))) {
rv = Preferences::GetInt(HTTP_PREF(
"speculative-parallel-limit"), &val);
if (NS_SUCCEEDED(rv)) {
mParallelSpeculativeConnectLimit = (uint32_t)std::clamp(val, 0, 1024);
}
}
// Whether or not to block requests for non head js/css items (e.g. media)
// while those elements load.
if (PREF_CHANGED(HTTP_PREF(
"rendering-critical-requests-prioritization"))) {
rv = Preferences::GetBool(
HTTP_PREF(
"rendering-critical-requests-prioritization"), &cVar);
if (NS_SUCCEEDED(rv)) mCriticalRequestPrioritization = cVar;
}
// on transition of network.http.diagnostics to true print
// a bunch of information to the console
if (pref && PREF_CHANGED(HTTP_PREF(
"diagnostics"))) {
rv = Preferences::GetBool(HTTP_PREF(
"diagnostics"), &cVar);
if (NS_SUCCEEDED(rv) && cVar) {
if (mConnMgr) mConnMgr->PrintDiagnostics();
}
}
if (PREF_CHANGED(HTTP_PREF(
"throttle.enable"))) {
rv = Preferences::GetBool(HTTP_PREF(
"throttle.enable"), &mThrottleEnabled);
if (NS_SUCCEEDED(rv) && mConnMgr) {
Unused << mConnMgr->UpdateParam(nsHttpConnectionMgr::THROTTLING_ENABLED,
static_cast<int32_t>(mThrottleEnabled));
}
}
if (PREF_CHANGED(HTTP_PREF(
"throttle.suspend-for"))) {
rv = Preferences::GetInt(HTTP_PREF(
"throttle.suspend-for"), &val);
mThrottleSuspendFor = (uint32_t)std::clamp(val, 0, 120000);
if (NS_SUCCEEDED(rv) && mConnMgr) {
Unused << mConnMgr->UpdateParam(
nsHttpConnectionMgr::THROTTLING_SUSPEND_FOR, mThrottleSuspendFor);
}
}
if (PREF_CHANGED(HTTP_PREF(
"throttle.resume-for"))) {
rv = Preferences::GetInt(HTTP_PREF(
"throttle.resume-for"), &val);
mThrottleResumeFor = (uint32_t)std::clamp(val, 0, 120000);
if (NS_SUCCEEDED(rv) && mConnMgr) {
Unused << mConnMgr->UpdateParam(
nsHttpConnectionMgr::THROTTLING_RESUME_FOR, mThrottleResumeFor);
}
}
if (PREF_CHANGED(HTTP_PREF(
"throttle.hold-time-ms"))) {
rv = Preferences::GetInt(HTTP_PREF(
"throttle.hold-time-ms"), &val);
mThrottleHoldTime = (uint32_t)std::clamp(val, 0, 120000);
if (NS_SUCCEEDED(rv) && mConnMgr) {
Unused << mConnMgr->UpdateParam(nsHttpConnectionMgr::THROTTLING_HOLD_TIME,
mThrottleHoldTime);
}
}
if (PREF_CHANGED(HTTP_PREF(
"throttle.max-time-ms"))) {
rv = Preferences::GetInt(HTTP_PREF(
"throttle.max-time-ms"), &val);
mThrottleMaxTime = (uint32_t)std::clamp(val, 0, 120000);
if (NS_SUCCEEDED(rv) && mConnMgr) {
Unused << mConnMgr->UpdateParam(nsHttpConnectionMgr::THROTTLING_MAX_TIME,
mThrottleMaxTime);
}
}
if (PREF_CHANGED(HTTP_PREF(
"send_window_size"))) {
Unused << Preferences::GetInt(HTTP_PREF(
"send_window_size"), &val);
mSendWindowSize = val >= 0 ? val : 0;
}
if (PREF_CHANGED(HTTP_PREF(
"on_click_priority"))) {
Unused << Preferences::GetBool(HTTP_PREF(
"on_click_priority"),
&mUrgentStartEnabled);
}
if (PREF_CHANGED(HTTP_PREF(
"tailing.enabled"))) {
Unused << Preferences::GetBool(HTTP_PREF(
"tailing.enabled"),
&mTailBlockingEnabled);
}
if (PREF_CHANGED(HTTP_PREF(
"tailing.delay-quantum"))) {
val = StaticPrefs::network_http_tailing_delay_quantum();
mTailDelayQuantum = (uint32_t)std::clamp(val, 0, 60000);
}
if (PREF_CHANGED(HTTP_PREF(
"tailing.delay-quantum-after-domcontentloaded"))) {
val = StaticPrefs::
network_http_tailing_delay_quantum_after_domcontentloaded();
mTailDelayQuantumAfterDCL = (uint32_t)std::clamp(val, 0, 60000);
}
if (PREF_CHANGED(HTTP_PREF(
"tailing.delay-max"))) {
val = StaticPrefs::network_http_tailing_delay_max();
mTailDelayMax = (uint32_t)std::clamp(val, 0, 60000);
}
if (PREF_CHANGED(HTTP_PREF(
"tailing.total-max"))) {
val = StaticPrefs::network_http_tailing_total_max();
mTailTotalMax = (uint32_t)std::clamp(val, 0, 60000);
}
if (PREF_CHANGED(HTTP_PREF(
"focused_window_transaction_ratio"))) {
float ratio = 0;
rv = Preferences::GetFloat(HTTP_PREF(
"focused_window_transaction_ratio"),
&ratio);
if (NS_SUCCEEDED(rv)) {
if (ratio > 0 && ratio < 1) {
mFocusedWindowTransactionRatio = ratio;
}
else {
NS_WARNING(
"Wrong value for focused_window_transaction_ratio");
}
}
}
//
// INTL options
//
if (PREF_CHANGED(INTL_ACCEPT_LANGUAGES)) {
// We don't want to set the new accept languages here since
// this pref is a complex type and it may be racy with flushing
// string resources.
mAcceptLanguagesIsDirty =
true;
}
//
// Tracking options
//
// Hint option
if (PREF_CHANGED(SAFE_HINT_HEADER_VALUE)) {
cVar =
false;
rv = Preferences::GetBool(SAFE_HINT_HEADER_VALUE, &cVar);
if (NS_SUCCEEDED(rv)) {
mSafeHintEnabled = cVar;
}
}
// toggle to true anytime a token bucket related pref is changed.. that
// includes telemetry and allow-experiments because of the abtest profile
bool requestTokenBucketUpdated =
false;
// "security.ssl3.ecdhe_rsa_aes_128_gcm_sha256" is the required h2 interop
// suite.
if (PREF_CHANGED(H2MANDATORY_SUITE)) {
cVar =
false;
rv = Preferences::GetBool(H2MANDATORY_SUITE, &cVar);
if (NS_SUCCEEDED(rv)) {
mH2MandatorySuiteEnabled = cVar;
}
}
// network.http.debug-observations
if (PREF_CHANGED(
"network.http.debug-observations")) {
cVar =
false;
rv = Preferences::GetBool(
"network.http.debug-observations", &cVar);
if (NS_SUCCEEDED(rv)) {
mDebugObservations = cVar;
}
}
if (PREF_CHANGED(HTTP_PREF(
"pacing.requests.enabled"))) {
rv = Preferences::GetBool(HTTP_PREF(
"pacing.requests.enabled"), &cVar);
if (NS_SUCCEEDED(rv)) {
mRequestTokenBucketEnabled = cVar;
requestTokenBucketUpdated =
true;
}
}
if (PREF_CHANGED(HTTP_PREF(
"pacing.requests.min-parallelism"))) {
rv =
Preferences::GetInt(HTTP_PREF(
"pacing.requests.min-parallelism"), &val);
if (NS_SUCCEEDED(rv)) {
mRequestTokenBucketMinParallelism =
static_cast<uint16_t>(std::clamp(val, 1, 1024));
requestTokenBucketUpdated =
true;
}
}
if (PREF_CHANGED(HTTP_PREF(
"pacing.requests.hz"))) {
rv = Preferences::GetInt(HTTP_PREF(
"pacing.requests.hz"), &val);
if (NS_SUCCEEDED(rv)) {
mRequestTokenBucketHz =
static_cast<uint32_t>(std::clamp(val, 1, 10000));
requestTokenBucketUpdated =
true;
}
}
if (PREF_CHANGED(HTTP_PREF(
"pacing.requests.burst"))) {
rv = Preferences::GetInt(HTTP_PREF(
"pacing.requests.burst"), &val);
if (NS_SUCCEEDED(rv)) {
mRequestTokenBucketBurst = val ? val : 1;
requestTokenBucketUpdated =
true;
}
}
if (requestTokenBucketUpdated) {
MakeNewRequestTokenBucket();
}
// Keepalive values for initial and idle connections.
if (PREF_CHANGED(HTTP_PREF(
"tcp_keepalive.short_lived_connections"))) {
rv = Preferences::GetBool(
HTTP_PREF(
"tcp_keepalive.short_lived_connections"), &cVar);
if (NS_SUCCEEDED(rv) && cVar != mTCPKeepaliveShortLivedEnabled) {
mTCPKeepaliveShortLivedEnabled = cVar;
}
}
if (PREF_CHANGED(HTTP_PREF(
"tcp_keepalive.short_lived_time"))) {
rv = Preferences::GetInt(HTTP_PREF(
"tcp_keepalive.short_lived_time"), &val);
if (NS_SUCCEEDED(rv) && val > 0) {
mTCPKeepaliveShortLivedTimeS = std::clamp(val, 1, 300);
// Max 5 mins.
}
}
if (PREF_CHANGED(HTTP_PREF(
"tcp_keepalive.short_lived_idle_time"))) {
rv = Preferences::GetInt(HTTP_PREF(
"tcp_keepalive.short_lived_idle_time"),
&val);
if (NS_SUCCEEDED(rv) && val > 0) {
mTCPKeepaliveShortLivedIdleTimeS = std::clamp(val, 1, kMaxTCPKeepIdle);
}
}
// Keepalive values for Long-lived Connections.
if (PREF_CHANGED(HTTP_PREF(
"tcp_keepalive.long_lived_connections"))) {
rv = Preferences::GetBool(HTTP_PREF(
"tcp_keepalive.long_lived_connections"),
&cVar);
if (NS_SUCCEEDED(rv) && cVar != mTCPKeepaliveLongLivedEnabled) {
mTCPKeepaliveLongLivedEnabled = cVar;
}
}
if (PREF_CHANGED(HTTP_PREF(
"tcp_keepalive.long_lived_idle_time"))) {
rv = Preferences::GetInt(HTTP_PREF(
"tcp_keepalive.long_lived_idle_time"),
&val);
if (NS_SUCCEEDED(rv) && val > 0) {
mTCPKeepaliveLongLivedIdleTimeS = std::clamp(val, 1, kMaxTCPKeepIdle);
}
}
if (PREF_CHANGED(HTTP_PREF(
"enforce-framing.http1")) ||
PREF_CHANGED(HTTP_PREF(
"enforce-framing.soft")) ||
PREF_CHANGED(HTTP_PREF(
"enforce-framing.strict_chunked_encoding"))) {
rv = Preferences::GetBool(HTTP_PREF(
"enforce-framing.http1"), &cVar);
if (NS_SUCCEEDED(rv) && cVar) {
mEnforceH1Framing = FRAMECHECK_STRICT;
}
else {
rv = Preferences::GetBool(
HTTP_PREF(
"enforce-framing.strict_chunked_encoding"), &cVar);
if (NS_SUCCEEDED(rv) && cVar) {
mEnforceH1Framing = FRAMECHECK_STRICT_CHUNKED;
}
else {
rv = Preferences::GetBool(HTTP_PREF(
"enforce-framing.soft"), &cVar);
if (NS_SUCCEEDED(rv) && cVar) {
mEnforceH1Framing = FRAMECHECK_BARELY;
}
else {
mEnforceH1Framing = FRAMECHECK_LAX;
}
}
}
}
if (PREF_CHANGED(HTTP_PREF(
"http2.default-hpack-buffer"))) {
mDefaultHpackBuffer =
StaticPrefs::network_http_http2_default_hpack_buffer();
}
if (PREF_CHANGED(HTTP_PREF(
"http3.default-qpack-table-size"))) {
rv = Preferences::GetInt(HTTP_PREF(
"http3.default-qpack-table-size"), &val);
if (NS_SUCCEEDED(rv)) {
mQpackTableSize = val;
}
}
if (PREF_CHANGED(HTTP_PREF(
"http3.default-max-stream-blocked"))) {
rv = Preferences::GetInt(HTTP_PREF(
"http3.default-max-stream-blocked"),
&val);
if (NS_SUCCEEDED(rv)) {
mHttp3MaxBlockedStreams = std::clamp(val, 0, 0xffff);
}
}
const bool imageAcceptPrefChanged = PREF_CHANGED(
"image.http.accept") ||
PREF_CHANGED(
"image.avif.enabled") ||
PREF_CHANGED(
"image.jxl.enabled");
if (imageAcceptPrefChanged) {
nsAutoCString userSetImageAcceptHeader;
if (Preferences::HasUserValue(
"image.http.accept")) {
rv = Preferences::GetCString(
"image.http.accept",
userSetImageAcceptHeader);
if (NS_FAILED(rv)) {
userSetImageAcceptHeader.Truncate();
}
}
if (userSetImageAcceptHeader.IsEmpty()) {
mImageAcceptHeader.Assign(ImageAcceptHeader());
}
else {
mImageAcceptHeader.Assign(userSetImageAcceptHeader);
}
}
if (PREF_CHANGED(
"network.http.accept") || imageAcceptPrefChanged) {
nsAutoCString userSetDocumentAcceptHeader;
if (Preferences::HasUserValue(
"network.http.accept")) {
rv = Preferences::GetCString(
"network.http.accept",
userSetDocumentAcceptHeader);
if (NS_FAILED(rv)) {
userSetDocumentAcceptHeader.Truncate();
}
}
if (userSetDocumentAcceptHeader.IsEmpty()) {
mDocumentAcceptHeader.Assign(DocumentAcceptHeader());
}
else {
mDocumentAcceptHeader.Assign(userSetDocumentAcceptHeader);
}
}
if (PREF_CHANGED(HTTP_PREF(
"http3.alt-svc-mapping-for-testing"))) {
nsAutoCString altSvcMappings;
rv = Preferences::GetCString(HTTP_PREF(
"http3.alt-svc-mapping-for-testing"),
altSvcMappings);
if (NS_SUCCEEDED(rv)) {
for (
const nsACString& tokenSubstring :
nsCCharSeparatedTokenizer(altSvcMappings,
',').ToRange()) {
nsAutoCString token{tokenSubstring};
int32_t index = token.Find(
";");
if (index != kNotFound) {
mAltSvcMappingTemptativeMap.InsertOrUpdate(
Substring(token, 0, index),
MakeUnique<nsCString>(Substring(token, index + 1)));
}
}
}
}
if (PREF_CHANGED(HTTP_PREF(
"http3.enable_qlog"))) {
// Initialize the directory.
nsCOMPtr<nsIFile> qlogDir;
if (Preferences::GetBool(HTTP_PREF(
"http3.enable_qlog")) &&
!mHttp3QlogDir.IsEmpty() &&
NS_SUCCEEDED(
NS_NewNativeLocalFile(mHttp3QlogDir, getter_AddRefs(qlogDir)))) {
// Here we do main thread IO, but since this only happens
// when enabling a developer feature it's not a problem for users.
rv = qlogDir->Create(nsIFile::DIRECTORY_TYPE, 0755);
if (NS_FAILED(rv) && rv != NS_ERROR_FILE_ALREADY_EXISTS) {
NS_WARNING(
"Creating qlog dir failed");
}
}
}
#ifdef XP_MACOSX
if (XRE_IsParentProcess()) {
if (PREF_CHANGED(HTTP_PREF(
"microsoft-entra-sso.enabled"))) {
rv =
Preferences::GetBool(HTTP_PREF(
"microsoft-entra-sso.enabled"), &cVar);
if (NS_SUCCEEDED(rv) && cVar) {
--> --------------------
--> maximum size reached
--> --------------------