Bug 1719070 - UA: Add support for a Nimbus experiment to override Firefox's User-Agent string. r=andreio,necko-reviewers,dragana

The ANDROID #ifdefs are necessary because Nimbus does not yet support Android in Gecko. The planned UA experiment won't include Android, so that's not a problem.

Differential Revision: https://phabricator.services.mozilla.com/D121112
This commit is contained in:
Chris Peterson 2021-08-08 01:07:51 +00:00
parent fef38775ad
commit 162a8620f1
4 changed files with 138 additions and 0 deletions

View File

@ -398,4 +398,63 @@ add_task(async function runOverrideTest() {
await testWorkerNavigator();
await testUserAgentHeader();
// Pop general.appname.override etc
await SpecialPowers.popPrefEnv();
// Pop privacy.resistFingerprinting
await SpecialPowers.popPrefEnv();
});
// Only test the Firefox and Gecko experiment prefs on desktop.
if (AppConstants.platform != "android") {
add_task(async function setupFirefoxVersionExperiment() {
// Mock Nimbus experiment settings
const { ExperimentFakes } = ChromeUtils.import(
"resource://testing-common/NimbusTestUtils.jsm"
);
let doExperimentCleanup = await ExperimentFakes.enrollWithFeatureConfig({
featureId: "firefox100",
value: { firefoxVersion: 100 },
});
let experimentOscpu;
switch (AppConstants.platform) {
case "win":
experimentOscpu =
cpuArch == "x86_64"
? "Windows NT 10.0; Win64; x64"
: "Windows NT 10.0";
break;
case "macosx":
experimentOscpu = "Macintosh; Intel Mac OS X 10.15";
break;
default:
experimentOscpu = "X11; Linux x86_64";
break;
}
let experimentUserAgent = `Mozilla/5.0 (${experimentOscpu}; rv:100.0) Gecko/20100101 Firefox/100.0`;
expectedResults = {
testDesc: "FirefoxVersionExperimentTest",
appVersion: DEFAULT_APPVERSION[AppConstants.platform],
hardwareConcurrency: navigator.hardwareConcurrency,
oscpu: DEFAULT_OSCPU[AppConstants.platform],
platform: DEFAULT_PLATFORM[AppConstants.platform],
userAgentNavigator: experimentUserAgent,
userAgentHeader: experimentUserAgent,
};
await testNavigator();
// Skip worker tests due to intermittent bug 1713764. This is unlikely to be
// a scenario that affects users enrolled in our "Firefox 100" experiment.
// await testWorkerNavigator();
await testUserAgentHeader();
// Clear Nimbus experiment prefs and session data
await doExperimentCleanup();
});
}

View File

@ -98,6 +98,10 @@
# include "nsCocoaFeatures.h"
#endif
#ifndef ANDROID
# include "mozilla/browser/NimbusFeatures.h"
#endif // ANDROID
//-----------------------------------------------------------------------------
#include "mozilla/net/HttpChannelChild.h"
@ -124,6 +128,11 @@
#define NS_HTTP_PROTOCOL_FLAGS \
(URI_STD | ALLOWS_PROXY | ALLOWS_PROXY_HTTP | URI_LOADABLE_BY_ANYONE)
#ifndef ANDROID
# define UA_EXPERIMENT_NAME "firefox100"_ns
# define UA_EXPERIMENT_VAR "firefoxVersion"_ns
#endif // ANDROID
//-----------------------------------------------------------------------------
using mozilla::dom::Promise;
@ -132,6 +141,42 @@ namespace mozilla::net {
LazyLogModule gHttpLog("nsHttp");
#ifndef ANDROID
static void ExperimentUserAgentUpdated(const char* /* aNimbusPref */,
void* aUserData) {
MOZ_ASSERT(aUserData != nullptr);
nsACString* aExperimentUserAgent = static_cast<nsACString*>(aUserData);
// Is this user enrolled in the Firefox 100 experiment?
int firefoxVersion =
NimbusFeatures::GetInt(UA_EXPERIMENT_NAME, UA_EXPERIMENT_VAR, 0);
if (firefoxVersion <= 0) {
aExperimentUserAgent->SetIsVoid(true);
return;
}
const char uaFormat[] =
# ifdef XP_WIN
# ifdef HAVE_64BIT_BUILD
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:%d.0) Gecko/20100101 "
"Firefox/%d.0"
# else
"Mozilla/5.0 (Windows NT 10.0; rv:%d.0) Gecko/20100101 Firefox/%d.0"
# endif
# elif defined(XP_MACOSX)
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:%d.0) Gecko/20100101 "
"Firefox/%d.0"
# else
// Linux, Android, FreeBSD, etc
"Mozilla/5.0 (X11; Linux x86_64; rv:%d.0) Gecko/20100101 Firefox/%d.0"
# endif
;
aExperimentUserAgent->Truncate();
aExperimentUserAgent->AppendPrintf(uaFormat, firefoxVersion, firefoxVersion);
}
#endif // ANDROID
#ifdef ANDROID
static nsCString GetDeviceModelId() {
// Assumed to be running on the main thread
@ -233,6 +278,10 @@ nsHttpHandler::nsHttpHandler()
mUserAgentOverride.SetIsVoid(true);
#ifndef ANDROID
mExperimentUserAgent.SetIsVoid(true);
#endif // ANDROID
MOZ_ASSERT(!gHttpHandler, "HTTP handler already created!");
nsCOMPtr<nsIXULRuntime> runtime = do_GetService("@mozilla.org/xre/runtime;1");
@ -338,6 +387,13 @@ nsresult nsHttpHandler::Init() {
Preferences::RegisterPrefixCallbacks(nsHttpHandler::PrefsChanged,
gCallbackPrefs, this);
PrefsChanged(nullptr);
#ifndef ANDROID
// monitor Firefox Version Experiment enrollment
NimbusFeatures::OnUpdate(UA_EXPERIMENT_NAME, UA_EXPERIMENT_VAR,
ExperimentUserAgentUpdated, &mExperimentUserAgent);
#endif // ANDROID
Telemetry::ScalarSet(Telemetry::ScalarID::NETWORKING_HTTP3_ENABLED,
mHttp3Enabled);
@ -699,6 +755,14 @@ const nsCString& nsHttpHandler::UserAgent() {
return mUserAgentOverride;
}
#ifndef ANDROID
if (!mExperimentUserAgent.IsVoid()) {
LOG(("using Firefox 100 Experiment User-Agent : %s\n",
mExperimentUserAgent.get()));
return mExperimentUserAgent;
}
#endif // ANDROID
if (mUserAgentIsDirty) {
BuildUserAgent();
mUserAgentIsDirty = false;

View File

@ -647,6 +647,11 @@ class nsHttpHandler final : public nsIHttpProtocolHandler,
nsCString mUserAgent;
nsCString mSpoofedUserAgent;
nsCString mUserAgentOverride;
#ifndef ANDROID
nsCString mExperimentUserAgent;
#endif // ANDROID
bool mUserAgentIsDirty{true}; // true if mUserAgent should be rebuilt
bool mAcceptLanguagesIsDirty{true};

View File

@ -84,6 +84,16 @@ const FeatureManifest = {
},
},
},
firefox100: {
description: "Firefox User-Agent version",
isEarlyStartup: true,
variables: {
firefoxVersion: {
type: "int",
description: "Firefox version to spoof (or `0` to use default version)",
},
},
},
newtab: {
description: "The about:newtab page",
isEarlyStartup: true,