Bug 1513574 - Remove UserAgentOverrides.jsm and UserAgentUpdates.jsm r=michal

Also removes the UA cache attached to nsILoadGroup and nsIRequestContext and the "http-on-useragent-request" observer notification.
If overriding the user agent is needed "http-on-modify-request" is equally usable (but should be used rarely, for performance reasons). A better way is using nsIDocShell.customUserAgent.

Depends on D14750

Differential Revision: https://phabricator.services.mozilla.com/D14751

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Valentin Gosu 2019-09-16 15:21:36 +00:00
parent 44d8431a8c
commit b27c4726d0
23 changed files with 8 additions and 1351 deletions

View File

@ -13,7 +13,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1339722
/**
* Test for Bug 1339722
* 1. Wait for "http-on-useragent-request" for the iframe load.
* 1. Wait for "http-on-modify-request" for the iframe load.
* 2. In the observer, access it's window proxy to trigger DOMWindowCreated.
* 3. In the event handler, delete the iframe so that the frameloader would be
* destoryed in the middle of ReallyStartLoading.
@ -22,7 +22,10 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1339722
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
const TOPIC = "http-on-useragent-request";
// This topic used to be http-on-useragent-request, but that got removed in
// bug 1513574. on-modify-request is called around the same time, and should
// behave similarly.
const TOPIC = "http-on-modify-request";
let win;
Services.obs.addObserver({
observe(subject, topic, data) {

View File

@ -693,12 +693,6 @@ pref("dom.phonenumber.substringmatching.VE", 7);
pref("gfx.canvas.azure.backends", "skia");
// See ua-update.json.in for the packaged UA override list
pref("general.useragent.updates.enabled", true);
pref("general.useragent.updates.url", "https://dynamicua.cdn.mozilla.net/0/%APP_ID%");
pref("general.useragent.updates.interval", 604800); // 1 week
pref("general.useragent.updates.retry", 86400); // 1 day
// When true, phone number linkification is enabled.
pref("browser.ui.linkify.phone", false);

View File

@ -205,10 +205,6 @@ pref("security.remote_settings.crlite_filters.signer", "onecrl.content-signature
pref("general.useragent.compatMode.firefox", false);
// This pref exists only for testing purposes. In order to disable all
// overrides by default, don't initialize UserAgentOverrides.jsm.
pref("general.useragent.site_specific_overrides", true);
pref("general.config.obscure_value", 13); // for MCD .cfg files
pref("general.warnOnAboutConfig", true);

View File

@ -60,7 +60,6 @@ class RequestContext final : public nsIRequestContext, public nsITimerCallback {
uint64_t mID;
Atomic<uint32_t> mBlockingTransactionCount;
nsAutoPtr<SpdyPushCache> mSpdyCache;
nsCString mUserAgentOverride;
typedef nsCOMPtr<nsIRequestTailUnblockCallback> PendingTailRequest;
// Number of known opened non-tailed requets
@ -187,15 +186,6 @@ void RequestContext::SetSpdyPushCache(SpdyPushCache* aSpdyPushCache) {
uint64_t RequestContext::GetID() { return mID; }
const nsACString& RequestContext::GetUserAgentOverride() {
return mUserAgentOverride;
}
void RequestContext::SetUserAgentOverride(
const nsACString& aUserAgentOverride) {
mUserAgentOverride = aUserAgentOverride;
}
NS_IMETHODIMP
RequestContext::AddNonTailRequest() {
MOZ_ASSERT(NS_IsMainThread());

View File

@ -95,10 +95,4 @@ interface nsILoadGroup : nsIRequest
* the docShell has created the default request.)
*/
attribute nsLoadFlags defaultLoadFlags;
/**
* The cached user agent override created by UserAgentOverrides.jsm. Used
* for all sub-resource requests in the loadgroup.
*/
attribute ACString userAgentOverrideCache;
};

View File

@ -93,11 +93,6 @@ interface nsIRequestContext : nsISupports
*/
[notxpcom,nostdcall] attribute SpdyPushCachePtr spdyPushCache;
/**
* This holds a cached value of the user agent override.
*/
[notxpcom,nostdcall] attribute ACString userAgentOverride;
/**
* Increases/decrease the number of non-tailed requests in this context.
* If the count drops to zero, all tail-blocked callbacks are notified

View File

@ -715,19 +715,6 @@ nsLoadGroup::SetDefaultLoadFlags(uint32_t aFlags) {
return NS_OK;
}
NS_IMETHODIMP
nsLoadGroup::GetUserAgentOverrideCache(nsACString& aUserAgentOverrideCache) {
aUserAgentOverrideCache = mUserAgentOverrideCache;
return NS_OK;
}
NS_IMETHODIMP
nsLoadGroup::SetUserAgentOverrideCache(
const nsACString& aUserAgentOverrideCache) {
mUserAgentOverrideCache = aUserAgentOverrideCache;
return NS_OK;
}
////////////////////////////////////////////////////////////////////////////////
void nsLoadGroup::TelemetryReport() {

View File

@ -87,8 +87,6 @@ class nsLoadGroup : public nsILoadGroup,
mozilla::TimeStamp mDefaultRequestCreationTime;
uint32_t mTimedRequests;
uint32_t mCachedRequests;
nsCString mUserAgentOverrideCache;
};
} // namespace net

View File

@ -485,8 +485,7 @@ HttpBaseChannel::SetLoadFlags(nsLoadFlags aLoadFlags) {
NS_IMETHODIMP
HttpBaseChannel::SetDocshellUserAgentOverride() {
// This sets the docshell specific user agent override, it will be overwritten
// by UserAgentOverrides.jsm if site-specific user agent overrides are set.
// This sets the docshell specific user agent override
nsresult rv;
nsCOMPtr<nsILoadContext> loadContext;
NS_QueryNotificationCallbacks(this, loadContext);

View File

@ -1,188 +0,0 @@
/* 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/. */
"use strict";
var EXPORTED_SYMBOLS = ["UserAgentOverrides"];
const { XPCOMUtils } = ChromeUtils.import(
"resource://gre/modules/XPCOMUtils.jsm"
);
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
const { UserAgentUpdates } = ChromeUtils.import(
"resource://gre/modules/UserAgentUpdates.jsm"
);
const PREF_OVERRIDES_ENABLED = "general.useragent.site_specific_overrides";
const MAX_OVERRIDE_FOR_HOST_CACHE_SIZE = 250;
// lazy load nsHttpHandler to improve startup performance.
XPCOMUtils.defineLazyGetter(this, "DEFAULT_UA", function() {
return Cc["@mozilla.org/network/protocol;1?name=http"].getService(
Ci.nsIHttpProtocolHandler
).userAgent;
});
var gPrefBranch;
var gOverrides = new Map();
var gUpdatedOverrides;
var gOverrideForHostCache = new Map();
var gInitialized = false;
var gOverrideFunctions = [
function(aHttpChannel) {
return UserAgentOverrides.getOverrideForURI(aHttpChannel.URI);
},
];
var gBuiltUAs = new Map();
var UserAgentOverrides = {
init: function uao_init() {
if (gInitialized) {
return;
}
gPrefBranch = Services.prefs.getBranch("general.useragent.override.");
gPrefBranch.addObserver("", buildOverrides);
Services.prefs.addObserver(PREF_OVERRIDES_ENABLED, buildOverrides);
try {
Services.obs.addObserver(
HTTP_on_useragent_request,
"http-on-useragent-request"
);
} catch (x) {
// The http-on-useragent-request notification is disallowed in content processes.
}
try {
UserAgentUpdates.init(function(overrides) {
gOverrideForHostCache.clear();
if (overrides) {
for (let domain in overrides) {
overrides[domain] = getUserAgentFromOverride(overrides[domain]);
}
overrides.get = function(key) {
return this[key];
};
}
gUpdatedOverrides = overrides;
});
buildOverrides();
} catch (e) {
// UserAgentOverrides is initialized before profile is ready.
// UA override might not work correctly.
}
Services.obs.notifyObservers(null, "useragentoverrides-initialized");
gInitialized = true;
},
addComplexOverride: function uao_addComplexOverride(callback) {
// Add to front of array so complex overrides have precedence
gOverrideFunctions.unshift(callback);
},
getOverrideForURI: function uao_getOverrideForURI(aURI) {
let host = aURI.asciiHost;
if (!gInitialized || (!gOverrides.size && !gUpdatedOverrides) || !host) {
return null;
}
let override = gOverrideForHostCache.get(host);
if (override !== undefined) {
return override;
}
function findOverride(overrides) {
let searchHost = host;
let userAgent = overrides.get(searchHost);
while (!userAgent) {
let dot = searchHost.indexOf(".");
if (dot === -1) {
return null;
}
searchHost = searchHost.slice(dot + 1);
userAgent = overrides.get(searchHost);
}
return userAgent;
}
override =
(gOverrides.size && findOverride(gOverrides)) ||
(gUpdatedOverrides && findOverride(gUpdatedOverrides));
if (gOverrideForHostCache.size >= MAX_OVERRIDE_FOR_HOST_CACHE_SIZE) {
gOverrideForHostCache.clear();
}
gOverrideForHostCache.set(host, override);
return override;
},
uninit: function uao_uninit() {
if (!gInitialized) {
return;
}
gInitialized = false;
gPrefBranch.removeObserver("", buildOverrides);
Services.prefs.removeObserver(PREF_OVERRIDES_ENABLED, buildOverrides);
Services.obs.removeObserver(
HTTP_on_useragent_request,
"http-on-useragent-request"
);
},
};
function getUserAgentFromOverride(override) {
let userAgent = gBuiltUAs.get(override);
if (userAgent !== undefined) {
return userAgent;
}
let [search, replace] = override.split("#", 2);
if (search && replace) {
userAgent = DEFAULT_UA.replace(new RegExp(search, "g"), replace);
} else {
userAgent = override;
}
gBuiltUAs.set(override, userAgent);
return userAgent;
}
function buildOverrides() {
gOverrides.clear();
gOverrideForHostCache.clear();
if (!Services.prefs.getBoolPref(PREF_OVERRIDES_ENABLED)) {
return;
}
let domains = gPrefBranch.getChildList("");
for (let domain of domains) {
let override = gPrefBranch.getCharPref(domain);
let userAgent = getUserAgentFromOverride(override);
if (userAgent != DEFAULT_UA) {
gOverrides.set(domain, userAgent);
}
}
}
function HTTP_on_useragent_request(aSubject, aTopic, aData) {
let channel = aSubject.QueryInterface(Ci.nsIHttpChannel);
for (let callback of gOverrideFunctions) {
let modifiedUA = callback(channel, DEFAULT_UA);
if (modifiedUA) {
channel.setRequestHeader("User-Agent", modifiedUA, false);
return;
}
}
}

View File

@ -1,333 +0,0 @@
/* 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/. */
"use strict";
var EXPORTED_SYMBOLS = ["UserAgentUpdates"];
const { AppConstants } = ChromeUtils.import(
"resource://gre/modules/AppConstants.jsm"
);
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
const { XPCOMUtils } = ChromeUtils.import(
"resource://gre/modules/XPCOMUtils.jsm"
);
XPCOMUtils.defineLazyGlobalGetters(this, ["XMLHttpRequest"]);
ChromeUtils.defineModuleGetter(
this,
"FileUtils",
"resource://gre/modules/FileUtils.jsm"
);
ChromeUtils.defineModuleGetter(
this,
"NetUtil",
"resource://gre/modules/NetUtil.jsm"
);
ChromeUtils.defineModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm");
ChromeUtils.defineModuleGetter(
this,
"UpdateUtils",
"resource://gre/modules/UpdateUtils.jsm"
);
XPCOMUtils.defineLazyServiceGetter(
this,
"gUpdateTimer",
"@mozilla.org/updates/timer-manager;1",
"nsIUpdateTimerManager"
);
XPCOMUtils.defineLazyGetter(this, "gDecoder", function() {
return new TextDecoder();
});
XPCOMUtils.defineLazyGetter(this, "gEncoder", function() {
return new TextEncoder();
});
const TIMER_ID = "user-agent-updates-timer";
const PREF_UPDATES = "general.useragent.updates.";
const PREF_UPDATES_ENABLED = PREF_UPDATES + "enabled";
const PREF_UPDATES_URL = PREF_UPDATES + "url";
const PREF_UPDATES_INTERVAL = PREF_UPDATES + "interval";
const PREF_UPDATES_RETRY = PREF_UPDATES + "retry";
const PREF_UPDATES_TIMEOUT = PREF_UPDATES + "timeout";
const PREF_UPDATES_LASTUPDATED = PREF_UPDATES + "lastupdated";
const KEY_PREFDIR = "PrefD";
const KEY_APPDIR = "XCurProcD";
const FILE_UPDATES = "ua-update.json";
const PREF_APP_DISTRIBUTION = "distribution.id";
const PREF_APP_DISTRIBUTION_VERSION = "distribution.version";
var gInitialized = false;
function readChannel(url) {
return new Promise((resolve, reject) => {
try {
let channel = NetUtil.newChannel({
uri: url,
loadUsingSystemPrincipal: true,
});
channel.contentType = "application/json";
NetUtil.asyncFetch(channel, (inputStream, status) => {
if (!Components.isSuccessCode(status)) {
reject();
return;
}
let data = JSON.parse(
NetUtil.readInputStreamToString(inputStream, inputStream.available())
);
resolve(data);
});
} catch (ex) {
reject(
new Error(
"UserAgentUpdates: Could not fetch " +
url +
" " +
ex +
"\n" +
ex.stack
)
);
}
});
}
var UserAgentUpdates = {
init(callback) {
if (gInitialized) {
return;
}
gInitialized = true;
this._callback = callback;
this._lastUpdated = 0;
this._applySavedUpdate();
Services.prefs.addObserver(PREF_UPDATES, this);
},
uninit() {
if (!gInitialized) {
return;
}
gInitialized = false;
Services.prefs.removeObserver(PREF_UPDATES, this);
},
_applyUpdate(update) {
// Check pref again in case it has changed
if (update && this._getPref(PREF_UPDATES_ENABLED, false)) {
this._callback(update);
} else {
this._callback(null);
}
},
_applySavedUpdate() {
if (!this._getPref(PREF_UPDATES_ENABLED, false)) {
// remove previous overrides
this._applyUpdate(null);
return;
}
// try loading from profile dir, then from app dir
let dirs = [KEY_PREFDIR, KEY_APPDIR];
dirs
.reduce((prevLoad, dir) => {
let file = FileUtils.getFile(dir, [FILE_UPDATES], true).path;
// tryNext returns promise to read file under dir and parse it
let tryNext = () =>
OS.File.read(file).then(bytes => {
let update = JSON.parse(gDecoder.decode(bytes));
if (!update) {
throw new Error("invalid update");
}
return update;
});
// try to load next one if the previous load failed
return prevLoad ? prevLoad.catch(tryNext) : tryNext();
}, null)
.catch(ex => {
if (AppConstants.platform !== "android") {
// All previous (non-Android) load attempts have failed, so we bail.
throw new Error(
"UserAgentUpdates: Failed to load " +
FILE_UPDATES +
ex +
"\n" +
ex.stack
);
}
// Make one last attempt to read from the Fennec APK root.
return readChannel("resource://android/" + FILE_UPDATES);
})
.then(update => {
// Apply update if loading was successful
this._applyUpdate(update);
})
.catch(Cu.reportError);
this._scheduleUpdate();
},
_saveToFile(update) {
let file = FileUtils.getFile(KEY_PREFDIR, [FILE_UPDATES], true);
let path = file.path;
let bytes = gEncoder.encode(JSON.stringify(update));
OS.File.writeAtomic(path, bytes, { tmpPath: path + ".tmp" }).then(() => {
this._lastUpdated = Date.now();
Services.prefs.setCharPref(
PREF_UPDATES_LASTUPDATED,
this._lastUpdated.toString()
);
}, Cu.reportError);
},
_getPref(name, def) {
try {
switch (typeof def) {
case "number":
return Services.prefs.getIntPref(name);
case "boolean":
return Services.prefs.getBoolPref(name);
}
return Services.prefs.getCharPref(name);
} catch (e) {
return def;
}
},
_getParameters() {
return {
"%DATE%": function() {
return Date.now().toString();
},
"%PRODUCT%": function() {
return Services.appinfo.name;
},
"%APP_ID%": function() {
return Services.appinfo.ID;
},
"%APP_VERSION%": function() {
return Services.appinfo.version;
},
"%BUILD_ID%": function() {
return Services.appinfo.appBuildID;
},
"%OS%": function() {
return Services.appinfo.OS;
},
"%CHANNEL%": function() {
return UpdateUtils.UpdateChannel;
},
"%DISTRIBUTION%": function() {
return this._getPref(PREF_APP_DISTRIBUTION, "");
},
"%DISTRIBUTION_VERSION%": function() {
return this._getPref(PREF_APP_DISTRIBUTION_VERSION, "");
},
};
},
_getUpdateURL() {
let url = this._getPref(PREF_UPDATES_URL, "");
let params = this._getParameters();
return url.replace(/%[A-Z_]+%/g, function(match) {
let param = params[match];
// preserve the %FOO% string (e.g. as an encoding) if it's not a valid parameter
return param ? encodeURIComponent(param()) : match;
});
},
_fetchUpdate(url, success, error) {
let request = new XMLHttpRequest();
request.mozBackgroundRequest = true;
request.timeout = this._getPref(PREF_UPDATES_TIMEOUT, 60000);
request.open("GET", url, true);
request.overrideMimeType("application/json");
request.responseType = "json";
request.addEventListener("load", function() {
let response = request.response;
response ? success(response) : error();
});
request.addEventListener("error", error);
request.send();
},
_update() {
let url = this._getUpdateURL();
url &&
this._fetchUpdate(
url,
response => {
// success
// apply update and save overrides to profile
this._applyUpdate(response);
this._saveToFile(response);
this._scheduleUpdate(); // cancel any retries
},
response => {
// error
this._scheduleUpdate(true /* retry */);
}
);
},
_scheduleUpdate(retry) {
// only schedule updates in the main process
if (
Services.appinfo.processType !== Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT
) {
return;
}
let interval = this._getPref(PREF_UPDATES_INTERVAL, 604800 /* 1 week */);
if (retry) {
interval = this._getPref(PREF_UPDATES_RETRY, interval);
}
gUpdateTimer.registerTimer(TIMER_ID, this, Math.max(1, interval));
},
notify(timer) {
// timer notification
if (this._getPref(PREF_UPDATES_ENABLED, false)) {
this._update();
}
},
observe(subject, topic, data) {
switch (topic) {
case "nsPref:changed":
if (data === PREF_UPDATES_ENABLED) {
this._applySavedUpdate();
} else if (data === PREF_UPDATES_INTERVAL) {
this._scheduleUpdate();
} else if (data === PREF_UPDATES_LASTUPDATED) {
// reload from file if there has been an update
let lastUpdated = parseInt(
this._getPref(PREF_UPDATES_LASTUPDATED, "0"),
0
);
if (lastUpdated > this._lastUpdated) {
this._applySavedUpdate();
this._lastUpdated = lastUpdated;
}
}
break;
}
},
QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver, Ci.nsITimerCallback]),
};

View File

@ -123,11 +123,6 @@ IPDL_SOURCES += [
'PHttpChannel.ipdl',
]
EXTRA_JS_MODULES += [
'UserAgentOverrides.jsm',
'UserAgentUpdates.jsm',
]
include('/ipc/chromium/chromium-config.mozbuild')
FINAL_LIBRARY = 'xul'

View File

@ -444,9 +444,6 @@ nsresult nsHttpChannel::PrepareToConnect() {
// notify "http-on-modify-request" observers
CallOnModifyRequestObservers();
SetLoadGroupUserAgentOverride();
// Check if request was cancelled during on-modify-request or on-useragent.
if (mCanceled) {
return mStatus;
}
@ -512,8 +509,7 @@ void nsHttpChannel::HandleOnBeforeConnect() {
nsresult nsHttpChannel::OnBeforeConnect() {
nsresult rv;
// Check if request was cancelled during suspend AFTER on-modify-request or
// on-useragent.
// Check if request was cancelled during suspend AFTER on-modify-request
if (mCanceled) {
return mStatus;
}
@ -6197,9 +6193,7 @@ nsHttpChannel::CancelByURLClassifier(nsresult aErrorCode) {
// notify "http-on-modify-request" observers
CallOnModifyRequestObservers();
SetLoadGroupUserAgentOverride();
// Check if request was cancelled during on-modify-request or on-useragent.
// Check if request was cancelled during on-modify-request
if (mCanceled) {
return mStatus;
}
@ -9644,48 +9638,6 @@ void nsHttpChannel::MaybeWarnAboutAppCache() {
}
}
void nsHttpChannel::SetLoadGroupUserAgentOverride() {
nsCOMPtr<nsIURI> uri;
GetURI(getter_AddRefs(uri));
nsAutoCString uriScheme;
if (uri) {
uri->GetScheme(uriScheme);
}
// We don't need a UA for file: protocols.
if (uriScheme.EqualsLiteral("file")) {
gHttpHandler->OnUserAgentRequest(this);
return;
}
nsIRequestContextService* rcsvc = gHttpHandler->GetRequestContextService();
nsCOMPtr<nsIRequestContext> rc;
if (rcsvc) {
rcsvc->GetRequestContext(mRequestContextID, getter_AddRefs(rc));
}
nsAutoCString ua;
if (nsContentUtils::IsNonSubresourceRequest(this)) {
gHttpHandler->OnUserAgentRequest(this);
if (rc) {
GetRequestHeader(NS_LITERAL_CSTRING("User-Agent"), ua);
rc->SetUserAgentOverride(ua);
}
} else {
GetRequestHeader(NS_LITERAL_CSTRING("User-Agent"), ua);
// Don't overwrite the UA if it is already set (eg by an XHR with explicit
// UA).
if (ua.IsEmpty()) {
if (rc) {
SetRequestHeader(NS_LITERAL_CSTRING("User-Agent"),
rc->GetUserAgentOverride(), false);
} else {
gHttpHandler->OnUserAgentRequest(this);
}
}
}
}
// Step 10 of HTTP-network-or-cache fetch
void nsHttpChannel::SetOriginHeader() {
if (mRequestHead.IsGet() || mRequestHead.IsHead()) {

View File

@ -533,8 +533,6 @@ class nsHttpChannel final : public HttpBaseChannel,
void MaybeWarnAboutAppCache();
void SetLoadGroupUserAgentOverride();
void SetOriginHeader();
void SetDoNotTrack();

View File

@ -383,24 +383,6 @@ void nsHttpHandler::SetFastOpenOSSupport() {
mFastOpenSupported ? "" : "not"));
}
void nsHttpHandler::EnsureUAOverridesInit() {
MOZ_ASSERT(XRE_IsParentProcess());
MOZ_ASSERT(NS_IsMainThread());
static bool initDone = false;
if (initDone) {
return;
}
nsresult rv;
nsCOMPtr<nsISupports> bootstrapper =
do_GetService("@mozilla.org/network/ua-overrides-bootstrapper;1", &rv);
MOZ_ASSERT(bootstrapper);
MOZ_ASSERT(NS_SUCCEEDED(rv));
initDone = true;
}
nsHttpHandler::~nsHttpHandler() {
LOG(("Deleting nsHttpHandler [this=%p]\n", this));
@ -2064,11 +2046,6 @@ nsHttpHandler::NewProxiedChannel(nsIURI* uri, nsIProxyInfo* givenProxyInfo,
net_EnsurePSMInit();
}
if (XRE_IsParentProcess()) {
// Load UserAgentOverrides.jsm before any HTTP request is issued.
EnsureUAOverridesInit();
}
uint64_t channelId;
nsresult rv = NewChannelId(channelId);
NS_ENSURE_SUCCESS(rv, rv);

View File

@ -375,11 +375,6 @@ class nsHttpHandler final : public nsIHttpProtocolHandler,
NotifyObservers(chan, NS_HTTP_ON_STOP_REQUEST_TOPIC);
}
// Called by the channel and cached in the loadGroup
void OnUserAgentRequest(nsIHttpChannel* chan) {
NotifyObservers(chan, NS_HTTP_ON_USERAGENT_REQUEST_TOPIC);
}
// Called by the channel before setting up the transaction
void OnBeforeConnect(nsIHttpChannel* chan) {
NotifyObservers(chan, NS_HTTP_ON_BEFORE_CONNECT_TOPIC);
@ -487,8 +482,6 @@ class nsHttpHandler final : public nsIHttpProtocolHandler,
void SetFastOpenOSSupport();
void EnsureUAOverridesInit();
// Checks if there are any user certs or active smart cards on a different
// thread. Updates mSpeculativeConnectEnabled when done.
void MaybeEnableSpeculativeConnect();

View File

@ -181,15 +181,6 @@ interface nsIHttpProtocolHandler : nsIProxiedProtocolHandler
*/
#define NS_HTTP_ON_MAY_CHANGE_PROCESS_TOPIC "http-on-may-change-process"
/**
* Before an HTTP request corresponding to a channel with the LOAD_DOCUMENT_URI
* flag is sent to the server, this observer topic is notified. The observer of
* this topic can then choose to modify the user agent for this request before
* the request is actually sent to the server. Additionally, the modified user
* agent will be propagated to sub-resource requests from the same load group.
*/
#define NS_HTTP_ON_USERAGENT_REQUEST_TOPIC "http-on-useragent-request"
/**
* This topic is notified for every http channel right after it called
* OnStopRequest on its listener, regardless whether it was finished

View File

@ -3,8 +3,6 @@ support-files =
method.sjs
partial_content.sjs
rel_preconnect.sjs
user_agent.sjs
user_agent_update.sjs
set_cookie_xhr.sjs
reset_cookie_xhr.sjs
web_packaged_app.sjs
@ -67,10 +65,7 @@ support-files =
[test_rel_preconnect.html]
[test_redirect_ref.html]
[test_uri_scheme.html]
[test_user_agent_overrides.html]
[test_user_agent_updates.html]
skip-if = (verify && debug && os == 'mac')
[test_user_agent_updates_reset.html]
[test_viewsource_unlinkable.html]
[test_xhr_method_case.html]
[test_1331680.html]

View File

@ -1,247 +0,0 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=782453
-->
<head>
<title>Test for User Agent Overrides</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=782453">Mozilla Bug 782453</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test">
<script class="testbody" type="text/javascript">
const PREF_OVERRIDES_ENABLED = "general.useragent.site_specific_overrides";
const PREF_OVERRIDES_BRANCH = "general.useragent.override.";
const DEFAULT_UA = navigator.userAgent;
const UA_WHOLE_OVERRIDE = "DummyUserAgent";
const UA_WHOLE_EXPECTED = UA_WHOLE_OVERRIDE;
const UA_PARTIAL_FROM = "\\wozilla"; // /\wozilla
const UA_PARTIAL_SEP = "#";
const UA_PARTIAL_TO = UA_WHOLE_OVERRIDE;
const UA_PARTIAL_OVERRIDE = UA_PARTIAL_FROM + UA_PARTIAL_SEP + UA_PARTIAL_TO;
const UA_PARTIAL_EXPECTED = DEFAULT_UA.replace(new RegExp(UA_PARTIAL_FROM, 'g'), UA_PARTIAL_TO);
const UA_PARTIAL_FROM2 = "[0-9]+";
const UA_PARTIAL_TO2 = "number";
const UA_PARTIAL_OVERRIDE2 = UA_PARTIAL_FROM2 + UA_PARTIAL_SEP + UA_PARTIAL_TO2;
const UA_PARTIAL_EXPECTED2 = DEFAULT_UA.replace(new RegExp(UA_PARTIAL_FROM2, 'g'), UA_PARTIAL_TO2);
function testUAIFrame(host, expected, sameQ, message, testNavQ, navSameQ, navMessage, callback) {
let url = location.pathname;
url = host + url.slice(0, url.lastIndexOf('/')) + '/user_agent.sjs';
let ifr = document.createElement('IFRAME');
ifr.src = url;
document.getElementById('content').appendChild(ifr);
window.addEventListener("message", function recv(e) {
ok(sameQ == (e.data.header.includes(expected)), message);
if (testNavQ) {
ok(navSameQ == (e.data.nav.includes(expected)), navMessage);
}
window.removeEventListener("message", recv);
callback();
});
}
function testUAIFrameNoNav(host, expected, sameQ, message, callback) {
testUAIFrame(host, expected, sameQ, message, false, true, '', callback);
}
function testUA(options, callback) {
var [domain, override, test_hosts, expected] =
[options.domain, options.override, options.test_hosts, options.expected];
(function nextTest() {
let test_host = test_hosts.shift();
info("Overriding " + domain + " with " + override + " for " + test_host);
function is_subdomain(host) {
var [test_domain] = host.slice(host.lastIndexOf('/') + 1).split(':', 1);
return test_domain === domain || test_domain.endsWith('.' + domain);
}
var localhost = location.origin;
var overrideNavigator = is_subdomain(localhost);
var navigator_ua, test_ua;
if (overrideNavigator) {
navigator_ua = navigator.userAgent;
}
let url = location.pathname;
url = test_host + url.slice(0, url.lastIndexOf('/')) + '/user_agent.sjs';
let ifr = document.createElement('IFRAME');
ifr.src = url;
document.getElementById('content').appendChild(ifr);
window.addEventListener("message", function recv(e) {
test_ua = e.data.header;
SpecialPowers.pushPrefEnv({
set: [[PREF_OVERRIDES_BRANCH + domain, override]],
}, function () {
testUAIFrame(test_host, expected, true, 'Header UA not overridden at step ' + (++step), true,
true, 'Navigator UA not overridden at step ' + (++step), function () {
// clear the override pref to undo overriding the UA
SpecialPowers.pushPrefEnv({
clear: [[PREF_OVERRIDES_BRANCH + domain]],
}, function () {
testUAIFrameNoNav(test_host, test_ua, true, 'Header UA not restored at step ' + (++step), function() {
test_hosts.length ? nextTest() : callback();
});
});
});
});
window.removeEventListener("message", recv);
});
})();
}
var step = 0; // for logging
var tests = [
// should override both header and navigator.userAgent
{
domain: location.hostname,
override: UA_WHOLE_OVERRIDE,
test_hosts: [location.origin],
expected: UA_WHOLE_EXPECTED
},
// should support partial overrides
{
domain: location.hostname,
override: UA_PARTIAL_OVERRIDE,
test_hosts: [location.origin],
expected: UA_PARTIAL_EXPECTED
},
{
domain: location.hostname,
override: UA_PARTIAL_OVERRIDE2,
test_hosts: [location.origin],
expected: UA_PARTIAL_EXPECTED2
},
// should match domain and subdomains
{
domain: 'example.org',
override: UA_WHOLE_OVERRIDE,
test_hosts: ['http://example.org',
'http://test1.example.org',
'http://sub1.test1.example.org'],
expected: UA_WHOLE_EXPECTED
},
// should not match superdomains
{
domain: 'sub1.test1.example.org',
override: UA_WHOLE_OVERRIDE,
test_hosts: ['http://example.org',
'http://test1.example.org'],
expected: DEFAULT_UA
},
// should work with https
{
domain: 'example.com',
override: UA_WHOLE_OVERRIDE,
test_hosts: ['https://example.com',
'https://test1.example.com',
'https://sub1.test1.example.com'],
expected: UA_WHOLE_EXPECTED
},
];
// test that UA is not overridden when the 'site_specific_overrides' pref is off
function testInactive(callback) {
SpecialPowers.pushPrefEnv({
set: [
[PREF_OVERRIDES_ENABLED, false],
[PREF_OVERRIDES_BRANCH + location.hostname, UA_WHOLE_OVERRIDE]
]
}, function () {
testUAIFrame(location.origin, UA_WHOLE_OVERRIDE, false, 'Failed to disabled header UA override at step ' + (++step),
true, false, 'Failed to disable navigator UA override at step + ' + (++step), function () {
SpecialPowers.pushPrefEnv({
clear: [
[PREF_OVERRIDES_ENABLED],
[PREF_OVERRIDES_BRANCH + location.hostname]
]
}, callback);
});
});
}
function testPriority(callback) {
// foo.bar.com override should have priority over bar.com override
var tests = [
['example.org', 'test1.example.org', 'sub1.test1.example.org'],
['example.org', 'test1.example.org', 'sub2.test1.example.org'],
['example.org', 'test2.example.org', 'sub1.test2.example.org'],
['example.org', 'test2.example.org', 'sub2.test2.example.org'],
];
(function nextTest() {
var [level0, level1, level2] = tests.shift();
var host = 'http://' + level2;
SpecialPowers.pushPrefEnv({
set: [
[PREF_OVERRIDES_ENABLED, true],
[PREF_OVERRIDES_BRANCH + level1, UA_WHOLE_OVERRIDE]
]
}, function () {
// should use first override at this point
testUAIFrameNoNav(host, UA_WHOLE_EXPECTED, true, 'UA not overridden at step ' + (++step), function() {
// add a second override that should be used
testUA({
domain: level2,
override: UA_PARTIAL_OVERRIDE,
test_hosts: [host],
expected: UA_PARTIAL_EXPECTED
}, function () {
// add a third override that should not be used
testUA({
domain: level0,
override: UA_PARTIAL_OVERRIDE,
test_hosts: [host],
expected: UA_WHOLE_EXPECTED
}, tests.length ? nextTest : callback);
});
});
});
})();
}
function testOverrides(callback) {
SpecialPowers.pushPrefEnv({
set: [[PREF_OVERRIDES_ENABLED, true]]
}, function nextTest() {
testUA(tests.shift(), function() { tests.length ? nextTest() : callback() });
});
}
SimpleTest.waitForExplicitFinish();
SimpleTest.requestCompleteLog();
SimpleTest.requestLongerTimeout(5);
testOverrides(function() {
testInactive(function() {
testPriority(SimpleTest.finish)
});
});
</script>
</pre>
</body>
</html>

View File

@ -1,357 +0,0 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=897221
-->
<head>
<title>Test for User Agent Updates</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=897221">Mozilla Bug 897221</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test">
<script class="testbody" type="text/javascript">
const PREF_APP_UPDATE_TIMERMINIMUMDELAY = "app.update.timerMinimumDelay";
const PREF_UPDATES = "general.useragent.updates.";
const PREF_UPDATES_ENABLED = PREF_UPDATES + "enabled";
const PREF_UPDATES_URL = PREF_UPDATES + "url";
const PREF_UPDATES_INTERVAL = PREF_UPDATES + "interval";
const PREF_UPDATES_TIMEOUT = PREF_UPDATES + "timeout";
const DEFAULT_UA = navigator.userAgent;
const UA_OVERRIDE = "DummyUserAgent";
const UA_ALT_OVERRIDE = "AltUserAgent";
const UA_PARTIAL_FROM = "\\wozilla"; // /\wozilla
const UA_PARTIAL_SEP = "#";
const UA_PARTIAL_TO = UA_OVERRIDE;
const UA_PARTIAL_OVERRIDE = UA_PARTIAL_FROM + UA_PARTIAL_SEP + UA_PARTIAL_TO;
const UA_PARTIAL_EXPECTED = DEFAULT_UA.replace(new RegExp(UA_PARTIAL_FROM, 'g'), UA_PARTIAL_TO);
function getUA(host) {
var url = location.pathname;
url = host + url.slice(0, url.lastIndexOf('/')) + '/user_agent.sjs';
var xhr = new XMLHttpRequest();
xhr.open('GET', url, false); // sync request
xhr.send();
is(xhr.status, 200, 'request failed');
is(typeof xhr.response, 'string', 'invalid response');
return xhr.response;
}
function testUAIFrame(host, expected, sameQ, message, testNavQ, navSameQ, navMessage, callback) {
let url = location.pathname;
url = host + url.slice(0, url.lastIndexOf('/')) + '/user_agent.sjs';
let ifr = document.createElement('IFRAME');
ifr.src = url;
document.getElementById('content').appendChild(ifr);
window.addEventListener("message", function recv(e) {
ok(sameQ == (e.data.header.includes(expected)), message);
if (testNavQ) {
ok(navSameQ == (e.data.nav.includes(expected)), navMessage);
}
window.removeEventListener("message", recv);
callback();
});
}
function testUAIFrameNoNav(host, expected, sameQ, message, callback) {
testUAIFrame(host, expected, sameQ, message, false, true, '', callback);
}
const OVERRIDES = [
{
domain: 'example.org',
override: '%DATE%',
host: 'http://example.org'
},
{
domain: 'test1.example.org',
override: '%PRODUCT%',
expected: SpecialPowers.Services.appinfo.name,
host: 'http://test1.example.org'
},
{
domain: 'test2.example.org',
override: '%APP_ID%',
expected: SpecialPowers.Services.appinfo.ID,
host: 'http://test2.example.org'
},
{
domain: 'sub1.test1.example.org',
override: '%APP_VERSION%',
expected: SpecialPowers.Services.appinfo.version,
host: 'http://sub1.test1.example.org'
},
{
domain: 'sub2.test1.example.org',
override: '%BUILD_ID%',
expected: SpecialPowers.Services.appinfo.appBuildID,
host: 'http://sub2.test1.example.org'
},
{
domain: 'sub1.test2.example.org',
override: '%OS%',
expected: SpecialPowers.Services.appinfo.OS,
host: 'http://sub1.test2.example.org'
},
{
domain: 'sub2.test2.example.org',
override: UA_PARTIAL_OVERRIDE,
expected: UA_PARTIAL_EXPECTED,
host: 'http://sub2.test2.example.org'
},
];
function getServerURL() {
var url = location.pathname;
return location.origin + url.slice(0, url.lastIndexOf('/')) + '/user_agent_update.sjs?';
}
function getUpdateURL() {
var url = getServerURL();
var overrides = {};
overrides[location.hostname] = UA_OVERRIDE;
OVERRIDES.forEach(function (val) {
overrides[val.domain] = val.override;
});
url = url + encodeURIComponent(JSON.stringify(overrides)).replace(/%25/g, '%');
return url;
}
function testDownload(callback) {
var startTime = Date.now();
var url = getUpdateURL();
isnot(navigator.userAgent, UA_OVERRIDE, 'UA already overridden');
info('Waiting for UA update: ' + url);
chromeScript.sendAsyncMessage("notify-on-update");
SpecialPowers.pushPrefEnv({
set: [
[PREF_UPDATES_ENABLED, true],
[PREF_UPDATES_URL, url],
[PREF_UPDATES_TIMEOUT, 10000],
[PREF_UPDATES_INTERVAL, 1] // 1 second interval
]
});
function waitForUpdate() {
info("Update Happened");
testUAIFrameNoNav(location.origin, UA_OVERRIDE, true, 'Header UA not overridden', function() {
var updateTime = parseInt(getUA('http://example.org'));
todo(startTime <= updateTime, 'Update was before start time');
todo(updateTime <= Date.now(), 'Update was after present time');
let overs = OVERRIDES;
(function nextOverride() {
val = overs.shift();
if (val.expected) {
testUAIFrameNoNav(val.host, val.expected, true, 'Incorrect URL parameter: ' + val.override, function() {
overs.length ? nextOverride() : callback();
});
} else {
nextOverride();
}
})();
});
}
chromeScript.addMessageListener("useragent-update-complete", waitForUpdate);
}
function testBadUpdate(callback) {
var url = getServerURL() + 'invalid-json';
var prevOverride = navigator.userAgent;
SpecialPowers.pushPrefEnv({
set: [
[PREF_UPDATES_URL, url],
[PREF_UPDATES_INTERVAL, 1] // 1 second interval
]
}, function () { setTimeout(function () {
var ifr = document.createElement('IFRAME');
ifr.src = "about:blank";
ifr.addEventListener('load', function() {
// We want to make sure a bad update doesn't cancel out previous
// overrides. We do this by waiting for 5 seconds (assuming the update
// occurs within 5 seconds), and check that the previous override hasn't
// changed.
is(navigator.userAgent, prevOverride,
'Invalid update deleted previous override');
callback();
});
document.getElementById('content').appendChild(ifr);
}, 5000); });
}
SimpleTest.waitForExplicitFinish();
SimpleTest.requestFlakyTimeout("Test sets timeouts to wait for updates to happen.");
SpecialPowers.pushPrefEnv({
set: [
[PREF_APP_UPDATE_TIMERMINIMUMDELAY, 0]
]
}, function () {
// Sets the OVERRIDES var in the chrome script.
// We do this to avoid code duplication.
chromeScript.sendAsyncMessage("set-overrides", OVERRIDES);
// testProfileLoad, testDownload, and testProfileSave must run in this order
// because testDownload depends on testProfileLoad and testProfileSave depends
// on testDownload to save overrides to the profile
chromeScript.sendAsyncMessage("testProfileLoad", location.hostname);
});
const chromeScript = SpecialPowers.loadChromeScript(_ => {
// Enter update timer manager test mode
Cc["@mozilla.org/updates/timer-manager;1"].getService(
Ci.nsIObserver).observe(null, "utm-test-init", "");
var _notifyOnUpdate = false;
const {FileUtils} = ChromeUtils.import("resource://gre/modules/FileUtils.jsm");
var FU = FileUtils;
const { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm");
var OSF = OS.File;
const KEY_PREFDIR = "PrefD";
const KEY_APPDIR = "XCurProcD";
const FILE_UPDATES = "ua-update.json";
const UA_OVERRIDE = "DummyUserAgent";
const UA_ALT_OVERRIDE = "AltUserAgent";
const PREF_UPDATES = "general.useragent.updates.";
const PREF_UPDATES_ENABLED = PREF_UPDATES + "enabled";
const PREF_UPDATES_LASTUPDATED = PREF_UPDATES + "lastupdated";
let { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
Services.prefs.addObserver(PREF_UPDATES_LASTUPDATED, () => {
if (_notifyOnUpdate) {
_notifyOnUpdate = false; // Only notify once, for the first update.
sendAsyncMessage("useragent-update-complete");
}
});
var OVERRIDES = null;
function is(value, expected, message) {
sendAsyncMessage("is-message", {value, expected, message});
}
function info(message) {
sendAsyncMessage("info-message", message);
}
function testProfileSave(hostname) {
info('Waiting for saving to profile');
var file = FU.getFile(KEY_PREFDIR, [FILE_UPDATES]).path;
(function waitForSave() {
OSF.exists(file).then(
(exists) => {
if (!exists) {
setTimeout(waitForSave, 100);
return;
}
return OSF.read(file).then(
(bytes) => {
info('Saved new overrides');
var decoder = new TextDecoder();
var overrides = JSON.parse(decoder.decode(bytes));
is(overrides[hostname], UA_OVERRIDE, 'Incorrect saved override');
OVERRIDES.forEach(function (val) {
val.expected && is(overrides[val.domain], val.expected,
'Incorrect saved override: ' + val.override);
});
sendAsyncMessage("testProfileSaveDone");
}
);
}
).catch(
(reason) => {
throw reason
}
);
})();
}
function testProfileLoad(hostname) {
var file = FU.getFile(KEY_APPDIR, [FILE_UPDATES]).path;
var encoder = new TextEncoder();
var overrides = {};
overrides[hostname] = UA_ALT_OVERRIDE;
var bytes = encoder.encode(JSON.stringify(overrides));
var badfile = FU.getFile(KEY_PREFDIR, [FILE_UPDATES]).path;
var badbytes = encoder.encode("null");
OSF.writeAtomic(file, bytes, {tmpPath: file + ".tmp"}).then(
() => OSF.writeAtomic(badfile, badbytes, {tmpPath: badfile + ".tmp"})
).then(
() => {
sendAsyncMessage("testProfileLoadDone");
},
(reason) => {
throw reason
}
);
}
addMessageListener("testProfileSave", testProfileSave);
addMessageListener("testProfileLoad", testProfileLoad);
addMessageListener("set-overrides", function(overrides) { OVERRIDES = overrides});
addMessageListener("notify-on-update", () => { _notifyOnUpdate = true });
}, { wantGlobalProperties: ["ChromeUtils", "TextEncoder", "TextDecoder"]});
chromeScript.addMessageListener("testProfileSaveDone", SimpleTest.finish);
chromeScript.addMessageListener("testProfileLoadDone", function() {
SpecialPowers.pushPrefEnv({
set: [[PREF_UPDATES_ENABLED, true]]
}, function () {
(function waitForLoad() {
var ifr = document.createElement('IFRAME');
ifr.src = location.origin;
ifr.addEventListener('load', function() {
var nav = ifr.contentWindow.navigator;
if (nav.userAgent !== UA_ALT_OVERRIDE) {
setTimeout(waitForLoad, 100);
return;
}
testUAIFrameNoNav(location.origin, UA_ALT_OVERRIDE, true, 'Did not apply saved override', function () {
testDownload(function() {
testBadUpdate(function() {
chromeScript.sendAsyncMessage("testProfileSave", location.hostname);
})
})
});
}, true);
document.getElementById('content').appendChild(ifr);
})();
});
});
chromeScript.addMessageListener("is-message", function(params) {
let {value, expected, message} = params;
is(value, expected, message);
});
chromeScript.addMessageListener("info-message", function(message) {
info(message);
});
</script>
</pre>
</body>
</html>

View File

@ -1,44 +0,0 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=942470
-->
<head>
<title>Test for Bug 942470</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=942470">Mozilla Bug 942470</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script class="testbody" type="text/javascript">
/** Test for Bug 942470 **/
function getUA(host) {
var url = location.pathname;
url = host + url.slice(0, url.lastIndexOf('/')) + '/user_agent.sjs';
var xhr = new XMLHttpRequest();
xhr.open('GET', url, false); // sync request
xhr.send();
is(xhr.status, 200, 'request failed');
is(typeof xhr.response, 'string', 'invalid response');
return xhr.response;
}
const UA_OVERRIDE = "DummyUserAgent";
info("User agent is " + navigator.userAgent);
isnot(navigator.userAgent, UA_OVERRIDE,
"navigator.userAgent is not reverted");
isnot(getUA(location.origin), UA_OVERRIDE,
"User-Agent is not reverted");
</script>
</pre>
</body>
</html>

View File

@ -1,21 +0,0 @@
function handleRequest(request, response)
{
// avoid confusing cache behaviors
response.setHeader("Cache-Control", "no-cache", false);
response.setHeader("Content-Type", "text/html", false);
response.setHeader("Access-Control-Allow-Origin", "*", false);
// used by test_user_agent tests
response.write(
"<html><body>\
<script type='text/javascript'>\
var msg = {\
header: '" + request.getHeader('User-Agent') + "',\
nav: navigator.userAgent\
};\
self.parent.postMessage(msg, '*');\
</script>\
</body></html>"
);
}

View File

@ -1,10 +0,0 @@
function handleRequest(request, response)
{
// avoid confusing cache behaviors
response.setHeader("Cache-Control", "no-cache", false);
response.setHeader("Content-Type", "application/json", false);
// used by test_user_agent_updates test
response.write(decodeURIComponent(request.queryString));
}